40道经典java多线程面试题
40道经典java多线程面试题
题目来源
看完了java并发编程的艺术,自认为多线程“大成”,然后找了一些面试题,也发现了一些不足。
一下问题来源于网上的博客,答案均为本人个人见解,若有疑问或错误欢迎讨论。QQ:1807812486
1.什么是线程
线程是操作系统能够进行运算调度的最小单位,它被包含在进程当中,是进程的实际运作单位。
2.线程和进程的区别
一个进程是一个独立的运行环境,它可以被看做是一个程序或应用。线程是进程中执行的一个任务。线程是进程的子集,一个进程可以有多个线程。进程独占一片内存,进程共享内存。
3.CyclicBarrier和CountDownLatch有什么不同
CountDownLatch和CyclicBarrier都用于让线程等待,达到一定条件时再运行。CountDownlatch更加灵活可以在一个线程中多次使用,但是不能重置计数器。CyclicBarrier更加强大,可以重置计数器reset(),可以检查被阻塞的数目,可以检查被阻塞的线程是否被中断。
4.java内存模型是什么(JMM java Memory Model)
java内存模型规定和指引java程序在不同的内存架构,CPU,操作系统中有确定性的行为。JMM定义了线程与主存之间的关系,JMM控制一个线程的共享变量的写入何时对另一个线程可见。
5.happens-before原则
- 程序顺序规则:一个线程中的任意操作happens-before该线程中的任意后续操作
- 监视器锁原则:对于一个锁的解锁happens-before该锁的加锁。
- volatile变量原则:volatile写happens-before volatile读
- 传递性原则:A happens-before B B happens-before C A happens-before C.
A happens-before B 并不是 A比B先执行,而是A的执行结果对B可见。且 A按顺序排在B之前
6.Java中的volatile变量是什么
Volatile是成员变量的修饰符,能够保证该变量在多线程中的可见性。实现原理是将写缓存刷新到主存中,然后根据缓存一致性原则保证可见性。
7.什么是线程安全?Vector是一个安全类吗
在多线程中每一次读的数据和预期数据一样就是线程安全(此描述不够准确)
8.如何实现两个线程间共享数据
1.通过类变量直接将数据放到主存中
2.通过并发的数据结构来存储数据
3.使用volatile变量或者锁
4.调用atomic类
9.为什么wait(),notify(),notifyAll()在对象中,而不在Thread类中
java中锁的级别是对象级而不是线程级,每个对象都有锁,通过线程获得。如果wait()方法在线程中,线程正在等待的是哪个锁就不明显了。
10.什么是ThreadLocal变量
ThreadLocal变量是一种特殊的变量,它可以将变量保存副本ThreadLocalMap中供每个线程单独使用。
ThreadLocal 详细解析
1.ThreadLocal的结构
成员变量: threadLocalHashCode 用来区分不同的ThreadLocal
静态内部类:ThreadLocalMap 用来存储ThreadLocal
方法set(): 通过获取当前线程t,然后获得一个threadlocalmap。第一次使用ThreadLocal时会初始化一个ThreadLocalMap,然后复制给当前线程。
11.什么是futureTask
RunnableFuture继承了Runnable接口和Future接口。它可以取消和启动一个线程的运行,当调用该类的线程未完成时调用get()会阻塞调用线程,当线程已经完成时会返回结果。futureTask的底层实现原理是AQS
12.java中interrupt(),interrupted()和isInterrupted()的区别
interrupt()是一个成员方法,调用时会改变中断状态,但是不会中断线程,只有catch后会中断线程。
interrupted()是一个静态方法,会中断线程,中断状态为true。
isInterrupted()是一个成员方法,会中断线程,中断状态为false。
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return this.isInterrupted(false);
}
private native boolean isInterrupted(boolean var1);
上面是源码,也就是说都调用的是本地方法,只是传入的参数不一样而已。
13.为什么wait和notify方法要在同步块中调用?
wait和notify都需要获得对象锁
14.为什么你应该在循环中检查等待条件?
代码
因为处于等待的线程可能会被假唤醒和错误警报,如果不在循环中等待可能会因为没有满足条件就退出等待。比如说,退出等待的条件是list()不为空,有7个线程在等待。当list为空时7个都将被唤醒,删除元素会报错。
15.同步集合和并发集合有什么不一样
同步集合的锁用的是sychronized,比较武断。并发集合的锁用的是AQS,灵活性更高,扩展性更强。
16. 有三个线程T1,T2,T3,怎么确保它们按顺序执行(确保main()方法所在的线程是Java程序最后结束的线程)?
使用CountDownLatch()和ClclicBarrier方法都可以实现main()方法最后执行。在前一个线程调用后一个线程的join()方法可以按顺序执行。
17.如果你提交任务时,线程池队列已满。会时发会生什么?
如果coreSize<maxSize 则会创建新的线程来执行任务。如果coreSize=MaxSize 会抛出一个RejectExecutionException.
18.你对线程优先级的理解是什么?
多线程其实是对CPU的轮询执行,只是CPU处理的速度太快了感觉不到上下文的切换。优先级就是CPU轮询线程的概率,优先级越高概率越高,优先级越低概率越低。
19.什么是线程池
线程池是管理多线程的一种工具,一个进程可以创建的线程数量是有限的,通过线程池创建的线程执行完成一个任务后不会立即关闭,而是继续执行阻塞队列中的线程直到阻塞线程为空。
20.有那些阻塞队列
ArrayBlockingQueue 一个由数组构成的有界阻塞队列
LinkedBlockingQueue 一个由链表构成的有界阻塞队列
PriorityBlockingQueue 一个支持优先级排序的无界阻塞队列
DelayQueue 一个使用优先队列实现的无界阻塞队列。
SynchroniouQueue 一个不储存元素的阻塞队列
LinkedTransferQueue 一个由链表结构组成的无界阻塞队列
LinkedBlockingDeque 一个由链表结构组成的双向阻塞队列
21.Java中synchronized 和 ReentrantLock 有什么不同?
synchronized是一个隐式的重入锁,比较笨重,实现方式是锁主存和缓存一致性。
reentrantLock是一个显示的重入锁,比较灵活,可以扩展为分段锁,实现方式是AQS.
22.如何避免死锁?
死锁发生的条件是
1.存在循环等待
2.存在资源竞争
3.不剥夺条件,已经获得的资源不会被剥夺
4.请求与保持,一个线程因请求资源被阻塞时,拥有资源的线程的状态不会改变。
避免死锁只需要破环其中的一个条件就可以了。
23. 怎么检测一个线程是否拥有锁?
Thread中有一个holdLock()方法,当且仅当当前线程拥有某个具体的对象锁时返回true
24.JVM中哪个参数是用来控制线程的栈堆栈小的
-Xss
25. Thread类中的yield方法有什么作用?
它会会通知cpu在此处可以更换切片,更不更换由cpu自己决定。(yield可以暂停当前正在执行的线程对象,让其他有线程执行)
26.Java中ConcurrentHashMap的并发度是什么?
并发度就是segment的个数,通常是2的N次方。默认是16
27.java中的Semaphore是什么
它是一个新的同步类,是一个计数信号。比如在数据库连接池中,假设我们只能获得10个数据库连接,20大小的线程池。此时我们就可以用一个Semaphore来表示数据库连接池的数目,当需要使用时就accquire()取得许可,使用完毕就release()添加一个许可。
28. 什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)?
线程调度器是一种操作系统服务,它负责给线程任务分配CPU处理的时间。时间分片指的是CPU一次性处理的一个任务的片段,分配CPU时间可以基于线程优先级或者线程等待时间,但是线程调度并不收到JVM的控制。
29.在多线程中,什么是上下文切换(context-switching)?
上线文切换是指CPU存储和恢复状态的过程,它使得线程能够从中断状态恢复继续运行。
30.如何在Java中创建Immutable对象?
将类和成员变量都设置为final,且成员为私有,成员的初始化通过构造参数初始化。不提供setter()方法。immutable的好处是在没有同步的情况下是线程安全的。
31. Java中的ReadWriteLock是什么?
它的名称是读写锁,实现原理是AQS.它将32位的 state状态变量分为前16位和后16位,前16位状态表示为读的状态,后16位状态表示为写的状态。也就是说读写锁有两把锁,一把控制读,一把控制写。写锁和写锁互斥,写锁和读锁互斥,读锁和互锁相容,写锁可以降级为读锁。
32.多线程中的忙循环是什么?
忙循环是指程序员用循环来代替wait()方法让线程进入等待,它的好处是可以占用CPU,不放弃缓存可以减少将数据读入缓存的时间。
33.volatile 变量和 atomic 变量有什么不同?
volatile的实现方式是将缓存中的值刷新到主存中,本生具有可见性和原子性但是volatile++不具有原子性。
atomic的实现方式是CAS.本质有点类似,也是缓存一致性原则。
34.单例模式的双检锁是什么?
当实例为空时,加锁再判断是否为空。如果为空则创建一个新的实例。
安全吗?不安全
原因:在创建一个对象的时候会分为三个步骤 1.为对象分配内存 2.在内存中初始化一个对象 3.将对象指配给使用者。步骤2依赖于1,所以不会被重排序。步骤3和步骤2没有依赖可能会被重排序。所以当另外一个线程在此时调用单例可能会获得一个空的实例。
35.如何在java中创建线程安全的Singleton
1.先加锁,再判断。
2.用一个volatile变量进行标识。
3.反射
36. 写出3条你遵循的多线程最佳实践
- 多用同步类,少用wait,notify
- 少用锁,应当缩小同步范围
- 给线程一个自己的名字
- 多用并发集合少用同步集合
37.如何强制启动一个线程?
我觉得没有方法强制启动,就像垃圾回收一样,即使调用了System.gc也不会马上就gc。因为java中的一个线程和操作系统中的一个线程是一一对应的。操作系统的线程什么时候在cpu上轮询由操作系统决定,jvm没有权限就像优先级一样。
38.java中的fork/join框架是什么
它是jdk7中的一款高效并发编程工具,它利用工作窃取算法将一个大任务划分为若干个子任务,然后将子任务结果合并起来,从而提高了并发编程的效率。
39.线程调度策略?
1.抢占式调度策略
处于就绪态的线程,根据优先级抢占进入运行态。
2.时间片轮转调度策略
所有处于就绪状态的线程中选择优先级最高的线程分配一定的CPU时间运行,该时间过后再选择其他线程运行。
40.在线程中你怎么处理不可捕捉异常?
hread.UncaughtExceptionHandler是java SE5中的新接口,它允许我们在每一个Thread对象上添加一个异常处理器。