Java 并发工具包(java.util.concurrent
)提供了一系列高效、灵活的同步工具,用于解决多线程环境下的复杂协作问题。以下从 特性、底层实现、使用场景、代码示例 四个维度,详细解析 CountDownLatch
、CyclicBarrier
、Semaphore
、Exchanger
四大工具。
1. CountDownLatch:一次性线程等待器
核心特性
• 单向同步 :主线程等待一组子线程完成任务,或子线程等待主线程发出启动信号。 • 一次性使用 :计数器归零后无法重置,需重新创建实例。 • 不可中断性 :await()
方法可响应中断,但计数器一旦归零无法回滚。
底层实现
• 基于 AQS(AbstractQueuedSynchronizer)共享模式 : • 状态变量 :AQS 的 state
字段保存计数器值。 • 同步逻辑 : ◦ countDown()
:调用 tryReleaseShared
,以 CAS 递减 state
。 ◦ await()
:调用 acquireSharedInterruptibly
,若 state != 0
,线程进入阻塞队列。 • 关键源码 : java // countDown() 实现 public void countDown() { sync.releaseShared(1); } // await() 实现 public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); }
使用场景
• 服务启动依赖 :主线程等待所有服务初始化完成。 • 压力测试 :模拟高并发场景,确保所有线程就绪后同时触发。 • 多任务汇总:等待多个异步任务结果汇总。
代码示例
csharp
public class ServiceInitializer {
public static void main(String[] args) throws InterruptedException {
int serviceCount = 3;
CountDownLatch latch = new CountDownLatch(serviceCount);
for (int i = 0; i < serviceCount; i++) {
new Thread(() -> {
try {
// 模拟服务初始化耗时
Thread.sleep(1000);
System.out.println("Service initialized: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown();
}
}).start();
}
// 主线程等待所有服务初始化完成
latch.await();
System.out.println("All services initialized. Starting main process.");
}
}
2. CyclicBarrier:可重置的多线程屏障
核心特性
• 循环使用 :计数器归零后自动重置,可重复使用。 • 多阶段协同 :支持分阶段任务处理,所有线程到达屏障后执行回调。 • 中断传播 :若某线程在 await()
时被中断或超时,其他线程会抛出 BrokenBarrierException
。
底层实现
• 基于 ReentrantLock
和 Condition
: • 核心字段 : ◦ count
:记录剩余未到达屏障的线程数。 ◦ generation
:表示当前屏障的"代",重置时创建新 generation
。 • 同步逻辑 : ◦ await()
:获取锁后递减 count
,若 count == 0
,执行回调并唤醒所有线程。 ◦ reset()
:打破当前屏障,唤醒所有线程并重置计数器。 • 关键源码 : java public int await() throws InterruptedException, BrokenBarrierException { // ... final ReentrantLock lock = this.lock; lock.lock(); try { final Generation g = generation; if (g.broken) throw new BrokenBarrierException(); if (Thread.interrupted()) { breakBarrier(); throw new InterruptedException(); } int index = --count; if (index == 0) { // 所有线程到达屏障 Runnable command = barrierCommand; if (command != null) { try { command.run(); } catch (Throwable ex) { breakBarrier(); throw ex; } } nextGeneration(); // 重置屏障 return 0; } // ... 线程阻塞逻辑 } finally { lock.unlock(); } }
使用场景
• 多阶段计算 :如图像处理中分阶段处理像素数据。 • 批量任务提交 :批量积累任务后统一提交到线程池。 • 数据分片处理:将大数据集分片,多线程处理完毕后合并结果。
代码示例
arduino
public class ParallelMatrixMultiplier {
private static final int ROWS = 1000;
private static final int COLS = 1000;
private static final int THREADS = 4;
private static final CyclicBarrier barrier = new CyclicBarrier(THREADS, () -> {
System.out.println("All rows processed. Merging results...");
});
public static void main(String[] args) {
double[][] matrix = new double[ROWS][COLS];
// 初始化矩阵...
for (int i = 0; i < THREADS; i++) {
final int threadId = i;
new Thread(() -> {
int startRow = threadId * (ROWS / THREADS);
int endRow = (threadId + 1) * (ROWS / THREADS);
for (int row = startRow; row < endRow; row++) {
// 处理矩阵的某一行
processRow(matrix[row]);
}
try {
barrier.await(); // 等待所有线程处理完成
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
private static void processRow(double[] row) {
// 模拟行处理逻辑
}
}
3. Semaphore:资源访问许可证管理器
核心特性
• 资源池化 :控制同时访问共享资源的线程数。 • 公平性策略 :支持公平模式(按等待顺序分配)和非公平模式(默认)。 • 动态调整 :支持运行时增减许可证数量(reducePermits()
和 increasePermits()
)。
底层实现
• 基于 AQS 共享模式 : • 状态变量 :AQS 的 state
字段表示可用许可证数量。 • 同步逻辑 : ◦ acquire()
:若 state > 0
则 CAS 递减,否则线程进入阻塞队列。 ◦ release()
:CAS 递增 state
,并唤醒等待线程。 • 关键源码 : java // 非公平模式获取许可证 final int nonfairTryAcquireShared(int acquires) { for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } }
使用场景
• 数据库连接池 :限制最大连接数,避免资源耗尽。 • API 限流 :限制每秒请求数,防止服务过载。 • 硬件设备控制:如限制同时访问打印机的线程数。
代码示例
java
public class ConnectionPool {
private static final int MAX_CONNECTIONS = 10;
private static final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS, true); // 公平模式
public Connection getConnection() throws InterruptedException {
semaphore.acquire(); // 获取许可证
return createConnection(); // 实际创建连接
}
public void releaseConnection(Connection conn) {
closeConnection(conn); // 实际关闭连接
semaphore.release(); // 释放许可证
}
private Connection createConnection() {
// 创建数据库连接
return null;
}
private void closeConnection(Connection conn) {
// 关闭数据库连接
}
}
4. Exchanger:线程间数据交换器
核心特性
• 成对交换 :必须有两个线程到达交换点才能完成数据交换。 • 泛型支持 :可交换任意类型对象。 • 超时控制 :支持带超时的 exchange(V x, long timeout, TimeUnit unit)
。
底层实现
• 基于 CAS 和线程等待队列 : • 核心字段 : ◦ slot
:保存当前等待交换的数据。 ◦ QNode
:链表结构管理等待线程。 • 同步逻辑 : ◦ exchange()
:若 slot
为空,将数据存入 slot
并自旋等待;否则取出对方数据并唤醒对方线程。 • 关键源码 : java public V exchange(V x) throws InterruptedException { Object v = doExchange(x == null ? NULL_ITEM : x, false, 0); if (v == NULL_ITEM) return null; if (v != CANCEL) return (V)v; Thread.interrupted(); // 重置中断状态 throw new InterruptedException(); }
使用场景
• 生产者-消费者缓冲区交换 :双缓冲区交替读写。 • 校对线程结果 :两个线程分别处理数据后交换验证。 • 流水线处理:多阶段处理线程间传递数据。
代码示例
arduino
public class ProducerConsumerWithExchanger {
private static final Exchanger<List<String>> exchanger = new Exchanger<>();
private static final int BUFFER_SIZE = 5;
public static void main(String[] args) {
new Thread(() -> { // 生产者
List<String> buffer = new ArrayList<>();
try {
while (true) {
for (int i = 0; i < BUFFER_SIZE; i++) {
buffer.add("Item-" + System.currentTimeMillis());
Thread.sleep(100); // 模拟生产耗时
}
System.out.println("Producer buffer full. Exchanging...");
buffer = exchanger.exchange(buffer); // 交换空缓冲区
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
new Thread(() -> { // 消费者
List<String> buffer = new ArrayList<>();
try {
while (true) {
if (buffer.isEmpty()) {
System.out.println("Consumer buffer empty. Exchanging...");
buffer = exchanger.exchange(buffer); // 交换满缓冲区
}
String item = buffer.remove(0);
System.out.println("Consumed: " + item);
Thread.sleep(200); // 模拟消费耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
总结与对比
工具 | 同步模型 | 重用性 | 适用场景 | 性能特点 |
---|---|---|---|---|
CountDownLatch | 主从协作 | 一次性 | 主线程等待多子线程完成初始化 | 低竞争下性能优异 |
CyclicBarrier | 多线程相互等待 | 可循环 | 分阶段并行计算 | 高并发下可能成为瓶颈 |
Semaphore | 资源访问控制 | 可循环 | 连接池、限流 | 公平模式性能略低于非公平 |
Exchanger | 线程间数据交换 | 可循环 | 双缓冲区交换、流水线处理 | 成对操作,吞吐量高 |
高级技巧与注意事项
- 避免死锁 : •
Exchanger
:确保成对线程调用exchange()
,避免单线程阻塞。 •Semaphore
:确保acquire()
和release()
成对出现,防止许可证泄漏。 - 性能优化 : •
CyclicBarrier
:回调任务应尽量简短,避免阻塞其他线程。 •CountDownLatch
:计数器值不宜过大,避免内存占用过高。 - 异常处理 : •
CyclicBarrier
:捕获BrokenBarrierException
处理线程中断或超时。 •Semaphore
:使用tryAcquire()
替代acquire()
避免长时间阻塞。
通过深入理解这些工具的底层原理和适用场景,开发者可以更高效地构建健壮、可扩展的并发系统。