Java基础面试题(1),Java教程视频免费下载

} 



七大参数



三大方法源码分析:



public static ExecutorService newSingleThreadExecutor() {

return new FinalizableDelegatedExecutorService

(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<Runnable>()));

}

public static ExecutorService newFixedThreadPool(int nThreads) {

return new ThreadPoolExecutor(5, 5,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<Runnable>());

}

public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

60L, TimeUnit.SECONDS,

new SynchronousQueue<Runnable>());

}

//三大方法都返回ThreadPoolExecutor()类




ThreadPoolExecutor(七大参数)源码分析:



public ThreadPoolExecutor(

int corePoolSize, // 核心线程池大小

int maximumPoolSize, // 最大核心线程池大小

long keepAliveTime, // 超时了没有人调用就会释放

TimeUnit unit, // 超时单位

BlockingQueue<Runnable> workQueue, // 阻塞队列

ThreadFactory threadFactory, // 线程工厂:创建线程的,一般不用动

RejectedExecutionHandler handle // 拒绝策略) {

    if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||

    keepAliveTime < 0)

    	throw new IllegalArgumentException();

    if (workQueue == null || threadFactory == null || handler == null)

    	throw new NullPointerException();

    this.acc = System.getSecurityManager() == null ?null:AccessController.getContext();

    this.corePoolSize = corePoolSize;

    this.maximumPoolSize = maximumPoolSize;

    this.workQueue = workQueue;

    this.keepAliveTime = unit.toNanos(keepAliveTime);

    this.threadFactory = threadFactory;

    this.handler = handler;

}




四种拒绝策略  

[白嫖资料]( )



image-20210527134117484

/**

  • new ThreadPoolExecutor.AbortPolicy() //银行满了,还有人进来,不处理这个人的,抛出异常

  • new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里!

  • new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!

  • new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的竞争,也不会抛出异常




### []( )线程生命周期(状态)



当线程被创建并启动以后,它**既不是一启动就进入了执行状态,也不是一直处于执行状态**



public enum State {

// 新生,此时仅由 JVM 为其分配内存,并初始化其成员变量的值

NEW,

// 调用了 start()方法之后,该线程处于运行Java 虚拟机会为其创建方法调用栈和程序计数器,等待调度运行

RUNNABLE,

// 阻塞

BLOCKED,

// 等待,死死地等

WAITING,

// 超时等待

TIMED_WAITING,

// 终止

TERMINATED;

}




### []( )终止线程方法



除了正常退出外有三种方法可以结束线程



*   设置退出标志,使线程正常退出,也就是当run()方法完成后线程终止

*   使用interrupt()方法中断线程

*   使用stop方法强行终止线程(不推荐使用)



**1.使用退出标志终止线程**



最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出



public class ThreadSafe extends Thread {

public volatile boolean exit = false; 

    public void run() { 

    while (!exit){

        //do something

    }

} 

}




**2.使用interrupt()方法中断当前线程**  

[白嫖资料]( )  

使用interrupt()方法来中断线程有两种情况:



1.  线程处于阻塞状态当调用线程的interrupt()方法时,会抛出InterruptException异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后break跳出循环状态,从而让我们有机会结束这个线程的执行。

2.  线程未处于阻塞状态,使用isInterrupted()判断线程的中断标志来退出循环。



代码演示:



//第一种情况

public class ThreadSafe extends Thread {

public void run() { 

    while (true){

        try{

                Thread.sleep(5*1000);//阻塞5妙

            }catch(InterruptedException e){

                e.printStackTrace();

                break;//捕获到异常之后,执行break跳出循环。

            }

    }

} 

}

//第二种情况

public class ThreadSafe extends Thread {

public void run() { 

    while (!isInterrupted()){

        //do something, but no throw InterruptedException

    }

} 

}




**3.使用stop方法终止线程**



程序中可以直接使用thread.stop()来强行终止线程,但是stop方法是很危险的



不安全主要是:



`thread.stop()`调用之后,创建子线程的线程就会抛出`ThreadDeatherror`的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用`thread.stop()`后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。



### []( )Java线程锁



#### []( )**乐观锁**



[白嫖资料]( )  

乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为 别人不会修改,所以不会上锁但是在更新的时候会判断一下在此期间别人有没有去**更新这个数 据,采取在写时先读出当前版本号,然后加锁操作,如果失败则要重复读-比较-写的操作。**



java 中的乐观锁基本都是通过 CAS 操作实现的,CAS 是一种更新的原子操作



#### []( )**悲观锁**



悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人 会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。



java中的悲观锁就是`Synchronized`



#### []( )**自旋锁**



如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁 的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋), 等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗



#### []( )**优缺点**



自旋锁_尽可能的减少线程的阻塞_,



这对于锁的竞争不激烈,且占用锁时间非常短的代码块来 说性能能大幅度的提升,因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗,这些操作会 导致线程发生两次上下文切换!



但是如果锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块,这时候就不适合 使用自旋锁了,因为自旋锁在获取锁前一直都是占用 cpu 做无用功,占着 XX 不 XX,同时有大量 线程在竞争一个锁,会导致获取锁的时间很长,线程自旋的消耗大于线程阻塞挂起操作的消耗, 其它需要 cup 的线程又不能获取到 cpu,造成 cpu 的浪费。所以这种情况下我们要关闭自旋锁;



#### []( )**Synchronized同步锁**



synchronized 它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁,同时属于可重 入锁。



#### []( )**Synchronized 作用范围**



[白嫖资料]( )



*   作用于方法时,锁住的是对象的实例(this);

*   当作用于静态方法时,锁住的是Class实例,此静态方法锁相当于类的一个全局锁, 会锁所有调用该方法的线程

*   synchronized 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。



#### []( )lock锁



//lock锁通常使用可重入锁ReentrantLock

Lock lock = new ReentrantLock();




ReentantLock 继承接口 Lock 并实现了接口中定义的方法,他是一种可重入锁,除了能完 成 synchronized 所能完成的所有工作外,还提供了诸如**可响应中断锁、可轮询锁请求、定时锁等 避免多线程死锁的方法**



public class SaleTicketDemo02 {

public static void main(String[] args) {

    // 并发:多线程操作同一个资源类, 把资源类丢入线程

    Ticket2 ticket = new Ticket2();

    // @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }

    new Thread(()->{for (int i = 1; i < 40 ; i++)

    ticket.sale();},"A").start();

    new Thread(()->{for (int i = 1; i < 40 ; i++)

    ticket.sale();},"B").start();

    new Thread(()->{for (int i = 1; i < 40 ; i++)

    ticket.sale();},"C").start();

}

}

// Lock三部曲

// 1、 new ReentrantLock();

// 2、 lock.lock(); // 加锁

// 3、 finally=> lock.unlock(); // 解锁

class Ticket2 {

// 属性、方法

private int number = 30;

Lock lock = new ReentrantLock();



public void sale(){

    lock.lock(); // 加锁

    try {

        // 业务代码

        if (number>0){

            System.out.println(Thread.currentThread().getName()+"卖出了"+

            (number--)+"票,剩余:"+number);

        }

    } catch (Exception e) {

        e.printStackTrace();

    } finally {

        lock.unlock(); // 解锁

    }

}

}




#### []( )synchronized 和 lock锁的区别



*   synchronized 是java内置的关键字,lock是一个java类

    

*   synchronized 无法获取锁的状态,lock 可以判断是否获得了锁

    

*   synchronized 会自动释放锁,lock不会自动释放锁,需要自己手动释放锁,如果不释放锁,会造成死锁

    

*   synchronized 线程一(获得锁,然后锁阻塞了),线程二(等待,然后还是傻傻的等待),lock锁就不一定会等待下去

    

*   synchronized 可重入锁,不可以被中断,非公平,lock 可重入锁,可判断锁,是否为公平锁,可以自行设置

    

*   synchronized 适合少量的代码同步问题,lock适合大量的代码同步问题

    



#### []( )公平锁与非公平锁



公平锁:十分公平:可以先来后到



非公平锁:十分不公平:可以插队 (默认)



#### []( )读写锁



[白嫖资料]( )  

为了提高性能,Java 提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,



如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。



读写锁分为读锁和写 锁,多个读锁不互斥,读锁与写锁互斥,这是由 jvm 自己控制的,你只要上好相应的锁即可。



**读锁**



如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁



**写锁**



如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上 读锁,写的时候上写锁!



// 加锁的

class MyCacheLock{

private volatile Map<String,Object> map = new HashMap<>();

// 读写锁: 更加细粒度的控制

private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

private Lock lock = new ReentrantLock();

// 存,写入的时候,只希望同时只有一个线程写

public void put(String key,Object value){

    readWriteLock.writeLock().lock();

    try {

        System.out.println(Thread.currentThread().getName()+"写入"+key);

        map.put(key,value);

        System.out.println(Thread.currentThread().getName()+"写入OK");

    } catch (Exception e) {

        e.printStackTrace();

    } finally {

        readWriteLock.writeLock().unlock();

    }

}

// 取,读,所有人都可以读!

public void get(String key){

    readWriteLock.readLock().lock();

    try {

        System.out.println(Thread.currentThread().getName()+"读取"+key);

        Object o = map.get(key);

        System.out.println(Thread.currentThread().getName()+"读取OK");

    } catch (Exception e) {

        e.printStackTrace();

    } finally {

        readWriteLock.readLock().unlock();

    }

}

}




[白嫖资料]( )



#### []( )锁优化



**减少锁持有时间**



只用在有线程安全要求的程序上加锁



**减小锁粒度**



将大对象(这个对象可能会被很多线程访问),拆成小对象,大大增加并行度,降低锁竞争。



**锁分离**



最常见的锁分离就是读写锁 ReadWriteLock,根据功能进行分离成读锁和写锁,这样读读不互 斥,读写互斥,写写互斥,即保证了线程安全,又提高了性能,



**锁粗化**



要求每个线程持有锁的时间尽量短,即在使用完 公共资源后,应该立即释放锁。



但是,凡事都有一个度,如果对同一个锁不停的进行请求、同步 和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化



**锁消除**



如果发现不可能被共享的对象,则可以消除这 些对象的锁操作



**死锁**



两个线程或两个以上线程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是这些线程都陷入了无限的等待中。



**如何避免线程死锁?**  

只要破坏产生死锁的四个条件中的其中一个就可以了。  

[白嫖资料]( )



*   破坏互斥条件:这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。

*   破坏请求与保持条件:一次性申请所有的资源。

*   破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

*   破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放



### []( )线程基本方法



线程相关的基本方法有 `wait`,`notify`,`notifyAll`,`sleep`,`join`,`yield` 等。



![](https://img-blog.csdnimg.cn/20210709171324444.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2OTcyODI2,size_16,color_FFFFFF,t_70)



#### []( )线程等待(wait)



调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断才会返回,需要注意的是调用 wait()方法后,会释放对象的锁。



#### []( )线程睡眠(sleep)



sleep 导致当前线程休眠,与 wait 方法不同的是 sleep 不会释放当前占有的锁,sleep(long)会导致线程进入 TIMED-WATING 状态,而 wait()方法会导致当前线程进入 WATING 状态



#### []( )线程让步(yield)



yield 会使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争 CPU 时间片。一般情况下,优先级高的线程有更大的可能性成功竞争得到 CPU 时间片



#### []( )线程中断(interrupt )



[白嫖资料]( )  

中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这个线程本身并不会因此而改变状态



#### []( )Join 等待其他线程终止



在当前线程中调用一个线程的 join() 方法,则当前线程转为阻塞状态,直到另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待 cpu 的宠幸。



#### []( )为什么要用 join() 方法 ?



很多情况下,主线程生成并启动了子线程,需要用到子线程返回的结果,也就是需要主线程需要在子线程结束后再结束,这时候就要用到 join() 方法。




### 最后

光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的**增加文章的篇幅**,减少文章的可读性

**[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](https://codechina.youkuaiyun.com/m0_60958482/java-p7)**

# Java面试宝典2021版

![](https://img-blog.csdnimg.cn/img_convert/0dedbaa6f88352cb5c8d8710c7081266.png)

![](https://img-blog.csdnimg.cn/img_convert/c3e3a58a13cb0055ddeb4cd1b12ad6d4.png)

# 最常见Java面试题解析(2021最新版)

![](https://img-blog.csdnimg.cn/img_convert/b40fa0dc2323d31e784194f7ae70ae26.png)

![](https://img-blog.csdnimg.cn/img_convert/4a51d74d001e77799461b115a657d5e8.png)

# 2021企业Java面试题精选

![](https://img-blog.csdnimg.cn/img_convert/e64f6ff4c8b9efeea3d5c8496b41a5b8.png)

(interrupt )



[白嫖资料]( )  

中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这个线程本身并不会因此而改变状态



#### []( )Join 等待其他线程终止



在当前线程中调用一个线程的 join() 方法,则当前线程转为阻塞状态,直到另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待 cpu 的宠幸。



#### []( )为什么要用 join() 方法 ?



很多情况下,主线程生成并启动了子线程,需要用到子线程返回的结果,也就是需要主线程需要在子线程结束后再结束,这时候就要用到 join() 方法。




### 最后

光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的**增加文章的篇幅**,减少文章的可读性

**[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](https://codechina.youkuaiyun.com/m0_60958482/java-p7)**

# Java面试宝典2021版

[外链图片转存中...(img-xMNNHjTn-1630553029765)]

[外链图片转存中...(img-yUgRwQdR-1630553029766)]

# 最常见Java面试题解析(2021最新版)

[外链图片转存中...(img-G845FwYL-1630553029767)]

[外链图片转存中...(img-4lCQcjTr-1630553029769)]

# 2021企业Java面试题精选

[外链图片转存中...(img-XlA7zX9K-1630553029770)]

![](https://img-blog.csdnimg.cn/img_convert/fa0dae276834a965250095b51b89b10f.png)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值