一、并发工具类概述
在 Java 多线程编程中,java.util.concurrent
包提供了一系列强大的并发工具类,这些工具类极大地简化了复杂并发场景的开发。它们主要分为以下几类:
- 同步辅助类 :如
CountDownLatch
、CyclicBarrier
、Semaphore
- 并发集合 :如
ConcurrentHashMap
、CopyOnWriteArrayList
- 原子类 :如
AtomicInteger
、AtomicReference
- 线程池相关 :如
ThreadPoolExecutor
、Executors
本文将重点探讨同步辅助类的核心实现原理和高级应用技巧。
二、CountDownLatch:线程计数器
核心原理
CountDownLatch
通过一个计数器实现线程间的同步。当一个或多个线程调用 await()
方法时,会阻塞等待计数器归零。其他线程通过调用 countDown()
方法减少计数器值。
典型应用场景
- 多任务并行执行:等待所有任务完成后执行汇总操作
- 资源预热:等待所有资源初始化完成后开始业务流程
- 性能测试:控制并发请求同时发起
实现细节
java
public class CountDownLatchExample {
private static final int THREAD_COUNT = 5;
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 开始执行");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " 执行完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}).start();
}
latch.await();
System.out.println("所有任务执行完毕");
}
}
扩展应用:实现超时等待
java
boolean allTasksDone = latch.await(5, TimeUnit.SECONDS);
if (!allTasksDone) {
// 处理超时逻辑
}
三、CyclicBarrier:循环屏障
核心原理
CyclicBarrier
允许一组线程在某个屏障点(barrier)等待彼此。当所有线程到达屏障点后,屏障打开,所有线程继续执行。与 CountDownLatch
不同,CyclicBarrier
可以重复使用。
典型应用场景
- 迭代计算:多线程迭代计算,每轮计算后等待所有线程同步
- 数据分片处理:多个线程处理不同数据分片,完成后汇总结果
- 并发测试:控制多个线程同时开始执行
实现细节
java
public class CyclicBarrierExample {
private static final int THREAD_COUNT = 3;
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT, () -> {
System.out.println("所有线程到达屏障点");
});
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 到达屏障点");
barrier.await();
System.out.println(Thread.currentThread().getName() + " 继续执行");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
高级特性:动态调整参与线程数
java
// 移除一个参与线程
barrier.reset();
四、Semaphore:信号量
核心原理
Semaphore
维护一组许可证(permit),通过控制许可证的数量来限制对共享资源的并发访问。线程获取许可证后才能执行操作,释放后其他线程才能获取。
典型应用场景
- 限流控制:限制同时访问数据库的连接数
- 资源池管理:管理线程池中的线程数量
- 公平性控制:保证线程获取许可证的顺序
实现细节
java
public class SemaphoreExample {
private static final int MAX_CONCURRENT = 2;
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(MAX_CONCURRENT);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 获得许可证");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 释放许可证");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}).start();
}
}
}
扩展应用:公平性与非公平性
java
// 公平性 Semaphore
Semaphore fairSemaphore = new Semaphore(MAX_CONCURRENT, true);
五、工具类对比与选择
工具类 | 核心功能 | 典型使用场景 | 线程安全机制 |
---|---|---|---|
CountDownLatch | 等待多个线程完成 | 任务汇总、资源初始化 | 计数器原子操作 |
CyclicBarrier | 线程间同步屏障 | 迭代计算、数据分片处理 | 重入锁 + 条件变量 |
Semaphore | 控制资源访问并发数 | 限流、资源池管理 | 基于 AQS 的状态机 |
六、并发工具类的性能优化
1. 减少上下文切换
java
// 使用带超时的 await 方法避免无限阻塞
boolean acquired = semaphore.tryAcquire(5, TimeUnit.SECONDS);
2. 批量操作优化
java
// 一次获取多个许可证
semaphore.acquire(3);
3. 结合线程池使用
java
ExecutorService executor = Executors.newFixedThreadPool(10);
CyclicBarrier barrier = new CyclicBarrier(10);
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
try {
barrier.await();
// 业务逻辑
} catch (Exception e) {
// 处理异常
}
});
}
七、实践案例:分布式系统中的锁机制
需求背景
某分布式系统需要控制同一资源的并发访问,确保最多 3 个节点同时操作。
实现方案
java
public class DistributedLock {
private static final int MAX_CONCURRENT = 3;
private static final Semaphore semaphore = new Semaphore(MAX_CONCURRENT);
public static void operateResource() {
try {
semaphore.acquire();
// 操作资源
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release();
}
}
}
优化方案
java
// 使用可中断锁避免死锁
semaphore.acquire(2); // 一次获取多个许可证
八、并发工具类的陷阱与解决方案
1. 线程泄漏
java
// 使用 try - finally 确保释放资源
try {
semaphore.acquire();
// 业务逻辑
} finally {
semaphore.release();
}
2. 虚假唤醒
java
// 在循环中检查条件
while (!semaphore.tryAcquire()) {
// 处理等待逻辑
}
3. 性能瓶颈
java
// 使用公平性策略
Semaphore fairSemaphore = new Semaphore(MAX_CONCURRENT, true);
九、总结
Java 并发工具类为多线程编程提供了强大的支持,合理使用这些工具可以显著提升代码的可读性和性能。在实际开发中,需要根据具体场景选择合适的工具类:
- CountDownLatch 适合"多对一"的同步场景
- CyclicBarrier 适合"多对多"的循环同步场景
- Semaphore 适合资源访问控制场景
通过结合线程池、原子类等工具,可以构建出高效、稳定的并发系统。同时,需要注意工具类的使用陷阱,避免因误用导致的性能问题或死锁。