你使用过哪些 Java 并发工具类?
你的回答(口语化,面试场景)
面试官:你使用过哪些 Java 并发工具类?
你:
好的,我结合项目经验来说说常用的并发工具类:
- CountDownLatch
- 作用:等所有线程就绪后再触发任务,或主线程等待子线程完成。
- 场景:压测时模拟高并发(比如100个请求同时发起)。
- 代码示例:
CountDownLatch latch = new CountDownLatch(3); // 三个子线程执行任务 executor.submit(() -> { doWork(); latch.countDown(); }); latch.await(); // 主线程阻塞等待 System.out.println("所有任务完成");
- CyclicBarrier
- 作用:让一组线程互相等待,达到屏障点后统一执行后续逻辑。
- 场景:多线程分阶段处理数据(比如先各自加载数据,再统一合并)。
- 代码示例:
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("全部就绪!")); executor.submit(() -> { loadData(); barrier.await(); // 等待其他线程 mergeData(); });
- Semaphore
- 作用:控制并发线程数(比如限流)。
- 场景:数据库连接池限制、接口限流(防止瞬时流量打满系统)。
- 代码示例:
Semaphore semaphore = new Semaphore(5); // 允许5个线程同时执行 if (semaphore.tryAcquire(2, TimeUnit.SECONDS)) { // 超时等待 try { accessDB(); } finally { semaphore.release(); } }
- ReentrantLock 和 StampedLock
- ReentrantLock:
- 可替代
synchronized
,支持公平锁、可中断锁。 - 场景:需要尝试获取锁(
tryLock()
)或避免死锁(比如支付超时回滚)。
- 可替代
- StampedLock:
- 读多写少场景优化,通过“乐观读”减少锁竞争。
- 场景:高频读、低频写的缓存(比如商品库存缓存)。
- 原子类(AtomicInteger 等)
- 作用:无锁线程安全操作(基于 CAS)。
- 场景:计数器(比如统计接口调用次数)、状态标志。
AtomicInteger counter = new AtomicInteger(0); counter.incrementAndGet(); // 线程安全自增
- Future 和 CompletableFuture
- Future:异步获取任务结果(需配合线程池)。
- CompletableFuture:
- 支持链式调用、组合多个异步任务(如
thenApply()
、allOf()
)。 - 场景:微服务并行调用(比如同时查询订单和用户信息)。
- 支持链式调用、组合多个异步任务(如
- 线程池工具类(Executors)
- 常用线程池:
newFixedThreadPool
:固定线程数,适用于稳定负载。newCachedThreadPool
:弹性线程数,适合短时异步任务。newScheduledThreadPool
:定时任务调度(替代 Timer)。
- 注意:阿里规约建议手动创建
ThreadPoolExecutor
,避免资源耗尽风险。
- 并发集合(ConcurrentHashMap、CopyOnWriteArrayList)
- ConcurrentHashMap:
- 分段锁(JDK7)或 CAS + synchronized(JDK8),高并发下替代 HashMap。
- 场景:全局缓存(比如电商首页类目数据)。
- CopyOnWriteArrayList:
- 写时复制,读多写少场景(比如监听器列表)。
预测面试官可能的追问及回答
追问1:CountDownLatch
和 CyclicBarrier
有什么区别?
回答:
- 触发机制:
CountDownLatch
通过countDown()
减计数,await()
阻塞直到计数归零,一次性使用。CyclicBarrier
通过await()
等待线程数达标后触发回调,可重复使用(reset()
)。
- 场景:
CountDownLatch
主等子,CyclicBarrier
子等子。
追问2:ReentrantLock
和 synchronized
怎么选?
回答:
- 优先
synchronized
:代码简洁,JVM自动管理锁。 - 需要高级功能时用
ReentrantLock
:比如尝试获取锁(tryLock
)、公平锁、可中断锁。
知识框架与底层原理补充
-
并发工具分类
| 类别 | 工具类 | 解决的核心问题 |
|--------------------|------------------------------------------|-------------------------------|
| 线程协作 |CountDownLatch
,CyclicBarrier
| 多线程步调协调 |
| 资源控制 |Semaphore
, 线程池 | 限制并发资源使用 |
| 锁机制 |ReentrantLock
,StampedLock
| 显式控制同步与互斥 |
| 线程安全容器 |ConcurrentHashMap
,CopyOnWriteArrayList
| 高并发下数据安全访问 |
| 异步任务 |Future
,CompletableFuture
| 非阻塞任务编排与结果获取 | -
底层原理
- AQS(AbstractQueuedSynchronizer):
ReentrantLock
、Semaphore
、CountDownLatch
均基于 AQS 实现,通过state
变量和 CLH 队列管理线程阻塞与唤醒。
- CAS(Compare-And-Swap):
- 原子类(如
AtomicInteger
)和ConcurrentHashMap
的线程安全操作依赖 CAS,避免锁竞争。
- 原子类(如
- 写时复制(Copy-On-Write):
CopyOnWriteArrayList
在写入时复制整个数组,保证读操作无锁,适合读多写极少场景。
- 最佳实践
- 避免死锁:
- 锁顺序一致、超时释放(
tryLock
)、使用并发集合替代手动同步。
- 锁顺序一致、超时释放(
- 性能优化:
- 读多写少用
StampedLock
,写多用ReentrantLock
。 - 短任务用
CompletableFuture
而非阻塞线程池。
- 读多写少用
- 线程池参数:
- 根据任务类型(CPU 密集型 vs IO 密集型)设置核心线程数(CPU 数 +1 或 2*CPU 数)。
- 高频面试题扩展
ConcurrentHashMap
在 JDK7 和 JDK8 的区别?- JDK7:分段锁(Segment),锁粒度粗。
- JDK8:CAS + synchronized 锁单个 Node,粒度更细。
CompletableFuture
的默认线程池问题?- 默认使用
ForkJoinPool.commonPool()
,建议自定义线程池避免业务阻塞公共池。
- 默认使用
通过理解这些工具类的设计意图和底层机制,你可以在面试中展现出对高并发场景的深刻掌控力!