Java 并发工具解析

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 线程间数据交换 可循环 双缓冲区交换、流水线处理 成对操作,吞吐量高

高级技巧与注意事项

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

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

相关推荐
福建佰胜张工7 分钟前
SMC 薄型气爪 MHF2-16D1 深度解析:平行开闭、低剖面结构在自动化装配中的选型与落地
java·服务器·安全·自动化
catoop10 分钟前
API Key 设计与安全生成方案:结构拆解、数据库设计与 Java 实现
java·数据库·安全
system_error_x38 分钟前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
驭渊的小故事1 小时前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring
Elastic 中国社区官方博客2 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1882 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn2 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su3 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
我命由我123454 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法
苜蓿muxu4 小时前
Java 字符串拼接:+、StringBuilder、StringBuffer的区别
java