Java 并发工具解析

Java 并发工具包(java.util.concurrent)提供了一系列高效、灵活的同步工具,用于解决多线程环境下的复杂协作问题。以下从 特性、底层实现、使用场景、代码示例 四个维度,详细解析 CountDownLatchCyclicBarrierSemaphoreExchanger 四大工具。


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

底层实现

基于 ReentrantLockCondition : • 核心字段 : ◦ 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 线程间数据交换 可循环 双缓冲区交换、流水线处理 成对操作,吞吐量高

高级技巧与注意事项

  1. 避免死锁 : • Exchanger :确保成对线程调用 exchange(),避免单线程阻塞。 • Semaphore :确保 acquire()release() 成对出现,防止许可证泄漏。
  2. 性能优化 : • CyclicBarrier :回调任务应尽量简短,避免阻塞其他线程。 • CountDownLatch:计数器值不宜过大,避免内存占用过高。
  3. 异常处理 : • CyclicBarrier :捕获 BrokenBarrierException 处理线程中断或超时。 • Semaphore :使用 tryAcquire() 替代 acquire() 避免长时间阻塞。

通过深入理解这些工具的底层原理和适用场景,开发者可以更高效地构建健壮、可扩展的并发系统。

相关推荐
在京奋斗者1 小时前
spring boot自动装配原理
java·spring boot·spring
明天不下雨(牛客同名)4 小时前
为什么 ThreadLocalMap 的 key 是弱引用 value是强引用
java·jvm·算法
多多*5 小时前
Java设计模式 简单工厂模式 工厂方法模式 抽象工厂模式 模版工厂模式 模式对比
java·linux·运维·服务器·stm32·单片机·嵌入式硬件
胡图蛋.6 小时前
Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?
java·spring boot·后端
牛马baby6 小时前
Java高频面试之并发编程-01
java·开发语言·面试
小小大侠客7 小时前
将eclipse中的web项目导入idea
java·eclipse·intellij-idea
不再幻想,脚踏实地7 小时前
MySQL(一)
java·数据库·mysql
吃海鲜的骆驼7 小时前
SpringBoot详细教程(持续更新中...)
java·spring boot·后端
迷雾骑士7 小时前
SpringBoot中WebMvcConfigurer注册多个拦截器(addInterceptors)时的顺序问题(二)
java·spring boot·后端·interceptor
别来无恙✲7 小时前
Mybatis源码分析
java·源码分析