在 Java 并发编程中,我使用过以下常用的并发工具类(均属于 java.util.concurrent
包),它们各自适用于不同的并发控制和线程协作场景:
✅ 1. CountDownLatch(倒计数锁存器)
📌 使用场景:
- 主线程等待多个子线程完成后再继续执行。
- 资源初始化完成后通知主线程启动。
📌 核心方法:
await()
:阻塞当前线程直到计数器归零。countDown()
:计数器减 1。
📌 特点:
- 一次性使用,计数器不可重置。
- 适用于一次性等待多个线程完成的场景。
示例代码:
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
latch.countDown();
}).start();
}
latch.await(); // 主线程等待
System.out.println("所有子线程已完成");
✅ 2. CyclicBarrier(循环屏障)
📌 使用场景:
- 多个线程互相等待,直到都到达某个屏障点后再一起继续执行。
- 多阶段并行计算,例如并行排序、分阶段任务。
📌 核心方法:
await()
:线程到达屏障点并等待。reset()
:重置屏障,进入下一轮同步。
📌 特点:
- 可重复使用,支持多轮同步。
- 可选执行一个“屏障动作”(Barrier Action)。
示例代码:
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有线程已到达屏障点"));
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 执行任务
barrier.await();
}).start();
}
✅ 3. Semaphore(信号量)
📌 使用场景:
- 控制同时访问的线程数量(如连接池、限流)。
- 实现互斥锁(许可数为 1)。
- 生产者-消费者模型中控制缓冲区的访问。
📌 核心方法:
acquire()
:获取许可,阻塞直到有空闲。release()
:释放许可。
📌 特点:
- 可设置为公平或非公平模式。
- 支持多次获取与释放。
示例代码:
Semaphore semaphore = new Semaphore(3); // 同时最多3个线程访问
semaphore.acquire();
try {
// 使用资源
} finally {
semaphore.release();
}
✅ 4. Exchanger(线程间交换器)
📌 使用场景:
- 两个线程之间交换数据(如双缓冲、流水线处理)。
📌 核心方法:
exchange(V data)
:线程等待另一个线程调用此方法,交换数据。
📌 特点:
- 用于两个线程间的数据交换。
- 适用于双缓冲、流水线、数据同步等场景。
示例代码:
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
String data = "from thread1";
try {
String other = exchanger.exchange(data);
System.out.println("收到:" + other);
} catch (InterruptedException e) { ... }
}).start();
new Thread(() -> {
String data = "from thread2";
try {
String other = exchanger.exchange(data);
System.out.println("收到:" + other);
} catch (InterruptedException e) { ... }
}).start();
✅ 5. Phaser(阶段同步器)
📌 使用场景:
- 多阶段任务同步,支持动态注册线程。
- 替代
CountDownLatch
和CyclicBarrier
,功能更灵活。
📌 核心方法:
arrive()
:线程到达阶段点。arriveAndAwaitAdvance()
:到达并等待其他线程完成当前阶段。register()
:动态注册线程。
📌 特点:
- 支持多阶段同步。
- 支持动态添加/移除线程。
- 更灵活,适用于复杂任务划分。
✅ 6. CompletableFuture(异步任务编排)
📌 使用场景:
- 异步编程、任务链式调用、组合多个异步任务结果。
- 替代传统的
Future
和回调地狱。
📌 核心方法:
supplyAsync()
/runAsync()
:异步执行任务。thenApply()
/thenAccept()
/thenRun()
:任务链式调用。thenCombine()
/allOf()
/anyOf()
:组合多个任务。
📌 特点:
- 支持异步非阻塞编程。
- 支持任务组合与异常处理。
- 是现代 Java 并发编程中非常强大的工具。
✅ 7. ReentrantLock(可重入锁)
📌 使用场景:
- 替代 synchronized,实现更灵活的锁机制。
- 尝试加锁、超时加锁、尝试获取锁失败时执行其他逻辑。
📌 核心方法:
lock()
/unlock()
:加锁与释放锁。tryLock()
:尝试加锁,不阻塞。
📌 特点:
- 支持公平锁与非公平锁。
- 支持尝试加锁、超时机制。
- 提供更细粒度的锁控制。
✅ 8. ReentrantReadWriteLock(读写锁)
📌 使用场景:
- 多线程读多写少的场景(如缓存、配置管理)。
- 读操作并发执行,写操作独占执行。
📌 核心方法:
readLock()
/writeLock()
:获取读锁或写锁。
📌 特点:
- 读共享,写独占。
- 提高并发性能,适用于读多写少的场景。
✅ 9. BlockingQueue(阻塞队列)
📌 使用场景:
- 生产者-消费者模型中的线程间通信。
- 线程池任务队列。
📌 常见实现类:
ArrayBlockingQueue
:有界队列。LinkedBlockingQueue
:无界队列。SynchronousQueue
:容量为 0,生产者必须等待消费者。
📌 核心方法:
put()
/take()
:阻塞式插入与取出。offer()
/poll()
:非阻塞方式。
✅ 10. ThreadLocalRandom / ForkJoinPool / CompletableFuture
- ThreadLocalRandom:线程安全的随机数生成器。
- ForkJoinPool:分治任务调度器,适用于并行任务拆分。
- CompletableFuture:异步任务编排工具(前面已介绍)。
📌 总结对比表
工具类 | 主要用途 | 是否可重用 | 是否支持超时 | 是否公平 | 适用场景 |
---|---|---|---|---|---|
CountDownLatch | 等待多个线程完成 | 否 | 是 | 否 | 主线程等待子线程 |
CyclicBarrier | 多线程到达屏障点后继续执行 | 是 | 是 | 否 | 多阶段任务同步 |
Semaphore | 控制并发资源访问 | 是 | 是 | 可选 | 资源池、限流、互斥 |
Exchanger | 两个线程交换数据 | 是 | 是 | 否 | 数据同步、双缓冲 |
Phaser | 多阶段任务同步,支持动态线程 | 是 | 是 | 否 | 多阶段并行任务 |
ReentrantLock | 可重入锁 | 是 | 是 | 可选 | 替代 synchronized,更灵活的锁控制 |
ReentrantReadWriteLock | 读写分离锁 | 是 | 是 | 可选 | 读多写少的并发场景 |
BlockingQueue | 线程间通信,任务队列 | 是 | 是 | 否 | 生产者-消费者模型 |
CompletableFuture | 异步任务编排 | 是 | 否 | 否 | 异步编程、任务链式调用 |
THE END