这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战
Java并发已经是Java面试必问的一块内容,对这块知道越多的细节,就越容易让面试官刮目相看。
我结合自身学习和面试经历,总结了Java并发的相关面试题,包括线程基础、多线程与并发、线程安全、线程池、锁、内存模型、JUC、集合与并发这几块内容。
因篇幅限制,分成三篇文章
中篇:内存模型、锁
下篇:JUC、集合与并发
JUC篇
对Java并发包有没有了解?
Java并发包指的是 java.util.concurrent(简称 JUC)包和其子包下的类和接口,为并发提供了各种功能支 持,比如:
- 锁机制类 Locks : Lock, Condition, ReadWriteLock
- 原子操作类Atomic : AtomicInteger
- 线程池相关类Executer : Future, Callable, Executor
- 信号量三组工具类Tools : CountDownLatch, CyclicBarrier, Semaphore
- 并发集合类Collections : CopyOnWriteArrayList, ConcurrentMap
常用的原子操作类有哪些?
- AtomicBoolean
- AtomicInteger
- AtomicLong
- LongAdder
- AtomicReference
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
原子操作类的底层实现原理是什么?
无锁技术,内部调用 Unsafe API中的CAS(Compare and Swap)方法:
- Unsafe API - Compare-And-Swap
- CPU硬件指令支持: CAS指令
两个要点:
- volatile的value变量保证可见性
- CAS操作保证写入不冲突
LongAdder 相比 AtomicLong有哪些改进,其实现原理是什么
采用了分段思想,支持更高的并发。
LongAdder extends Striped64;
transient volatile Cell[] cells;
public long sum() {
Cell[] cs = cells;
long sum = base;
if (cs != null) {
for (Cell c : cs)
if (c != null)
sum += c.value;
}
return sum;
}
Semaphore 是什么? 与锁有什么区别?
Semaphore 即信号量, 是一个计数信号,即允许N个许可。
- acquire() 方法,阻塞方式获取一个许可。
- release() 方法,释放一个许可。
如果信号量=1, 则等价于互斥锁。
如果信号量>1, 相当于共享锁。
用过 CountDownLatch 吗?
CountDownLatch(闭锁)可以看作一个只能做减法的计数器,可以让一个或多个线程等待执行。
场景: Master 线程等待 Worker 线程把任务执行完
示例:
- 等所有人干完手上的活,包工头宣布下班休息。
- 吃酒席: 大家围成一桌, 等剩下的座位数归0, 服务员才上菜。
重要方法:
public CountDownLatch(int count) // 构造方法(总数)
void await() throws InterruptedException // 阻塞并等待数量归0
boolean await(long timeout, TimeUnit unit) // 限时等待
void countDown() // 等待数减1
long getCount() // 返回剩余数量
特点:
- 采用减法计数,
- 各个子线程内countdown,
- 调用线程/主线程里await,作为聚合点,一直到计数为0
你对 CyclicBarrier 有什么了解?
CyclicBarrier(循环屏障), 可以让一组线程等待满足某个条件后同时执行。
CyclicBarrier 默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用 await() 方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞,直到 parties 个线程到达,结束阻塞。
使用场景: 任务执行到一定阶段, 等待其他任务对齐
示例:
- 组团去旅游, 到一个景点需要点名报数, 等人员到齐了才一起进场; 离开一个景点时也需要报数, 所 有人到齐之后才前往下一个景点。
- 吃酒席: 大家围成一桌, 满10人才开吃。
特点:
- 采用加法计数;
- 各个子线程内await,与主线程无关;
- 可以给CyclicBarrier加一个回调作为聚合点,此回调由前面的多个线程中的某个执行;
- 可以复用CyclicBarrier
用过 CompletableFuture 吗?跟Future有什么区别?
Future接口可以构建异步应用,但依然有其局限性。它很难直接表述多个Future 结果之间的依赖性。 实际开发中,我们经常需要达成以下目的:
- 将多个异步计算的结果合并成一个
- 等待Future集合中的所有任务都完成
- Future完成事件(即,任务完成以后触发执行动作)
在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的 复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。
它可能代表一个明确完成的Future,也有可能代表一个完成阶段( CompletionStage ),它支持在计 算完成以后触发一些函数或执行某些动作。它实现了Future和CompletionStage接口。
什么是 AQS?他的基本原理是什么?
AQS(AbstractQueuedSynchronizer) 是一个用来构建锁和同步的框架。
- Sync extends AbstractQueuedSynchronizer
- 抽象队列式的同步器
- Lock的底层实现原理两种资源共享方式: 独占 | 共享
- 子类负责实现公平 or 非公平
Java中各种常见的锁, 例如 ReentrantLock、ReadWriteLock,以及 Semaphore、CountDownLatch 等 等,都是基于 AQS 来构建的。
AQS 在内部定义了一个 volatile int state 变量,表示同步状态:
当线程调用 lock 方法时:
- 如果 state=0,说明没有任何线程占有这个锁,可以获得锁并将 state=1;
- 如果 state=1,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待。
用过 Condition 吗?
类比: Object monitor, wait/notify
示例:
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
集合与并发篇
经常使用哪些并发集合类?
- ConcurrentHashMap
- CopyOnWriteArrayList
- ConcurrentLinkedQueue
- ConcurrentLinkedDeque
- LinkedBlockingQueue
- DelayQueue
有哪些线程安全的集合类是有性能问题的?
- Collections 的 synchronizedList 等工具方法
- Vector
- Hashtable
- Stack
ConcurrentHashMap 有什么优势,内部实现原理是什么?
- JDK7的实现: 内部采用了分段锁的概念。
- JDK8的实现: 基于CAS、红黑树实现。
在合理hash的情况下, ConcurrentHashMap 的并发性能提升了很多倍。
ConcurrentHashMap 与 HashMap 相比有哪些不同?
- ConcurrentHashMap 是线程安全的
- HashMap 不是线程安全的
- ConcurrentHashMap 的 key 和 value 都不能为 null