详解java中的线程间协作工具:CountDownLatch,Semaphore,CyclicBarrier(二)

在Java并发编程中,CountDownLatchSemaphoreCyclicBarrier是三种常用的同步工具类,它们分别用于不同的线程协作场景。以下是它们的详细介绍:

一、CountDownLatch(倒计时门闩)

核心机制

  • 计数器同步:初始化时设置一个正整数计数器(如n),代表需等待的事件数量。线程通过await()阻塞,直到计数器归零;其他线程通过countDown()将计数器减1。
  • 一次性使用:计数器归零后无法重置,适合固定数量线程的同步。
  • 底层实现:基于AQS(AbstractQueuedSynchronizer),通过共享锁模式实现线程阻塞与唤醒。

核心方法

  • await():阻塞当前线程,直至计数器为0。
  • countDown():减少计数器值,当计数器归零时唤醒所有等待线程。
  • 支持超时版本await(long timeout, TimeUnit unit)避免无限等待。

典型场景

  • 并行任务同步:主线程等待多个子线程完成后再继续执行(如批量数据处理、多线程爬虫)。
  • 多线程协作测试:确保所有线程同时开始执行,测试结果更准确。

示例代码

CountDownLatch latch = new CountDownLatch(3);
// 工作线程
new Thread(() -> {
    // 任务执行
    latch.countDown(); // 任务完成后计数减1
}).start();
// 主线程等待
latch.await(); // 阻塞直到计数器归零
System.out.println("所有子任务已完成");

二、Semaphore(信号量)

核心机制

  • 许可证控制:初始化时设置许可证数量(如permits),线程通过acquire()获取许可证(计数器减1),使用后通过release()释放(计数器加1)。
  • 公平与非公平模式:公平模式按线程请求顺序分配许可证;非公平模式允许新线程优先获取。
  • 动态调整:许可证数量可通过release()动态增加。

核心方法

  • acquire():获取一个许可证,无可用时阻塞。
  • release():释放一个许可证,唤醒等待线程。
  • tryAcquire():尝试获取许可证,不阻塞。

典型场景

  • 资源池管理:限制数据库连接池、线程池的并发访问数量。
  • 限流控制:通过动态调整许可证数量控制API请求并发量。
  • 共享资源互斥:多个线程访问共享文件时,通过信号量控制同时写入数量。

示例代码

Semaphore semaphore = new Semaphore(3); // 3个许可证
// 工作线程
new Thread(() -> {
    try {
        semaphore.acquire(); // 获取许可证
        // 访问共享资源
    } finally {
        semaphore.release(); // 释放许可证
    }
}).start();

三、CyclicBarrier(循环屏障)

核心机制

  • 线程组同步:初始化时设置需等待的线程数量(如parties),线程通过await()阻塞,直到所有线程到达屏障点;屏障触发后,所有线程继续执行。
  • 可复用:屏障触发后计数器自动重置,可重复使用。
  • 屏障动作:支持在屏障触发时执行一个Runnable任务(如汇总结果)。

核心方法

  • await():阻塞当前线程,直至所有线程到达屏障点。
  • 支持超时版本await(long timeout, TimeUnit unit)避免无限等待。

典型场景

  • 多阶段任务协作:如并行算法中各子任务完成自身部分后,需在屏障处汇合并同步进行下一阶段计算。
  • 资源初始化同步:多个线程依赖共享资源初始化,初始化完成后统一放行业务逻辑。

示例代码

CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("所有线程已到达屏障点,继续执行后续操作");
});
// 工作线程
new Thread(() -> {
    try {
        // 执行任务
        barrier.await(); // 等待其他线程
        // 屏障触发后继续执行
    } catch (InterruptedException | BrokenBarrierException e) {
        e.printStackTrace();
    }
}).start();

皿、三者对比

对比维度CountDownLatchSemaphoreCyclicBarrier
核心目的等待特定数量线程完成控制并发访问资源的线程数让一组线程互相等待,达到同步点
计数器行为仅递减,归零后不可重置可递增/递减,支持动态调整递减至零后自动重置,可重复使用
复用性一次性使用可重复使用(如资源池)可重复使用(如多阶段任务同步)
典型场景线程同步、任务聚合限流、资源互斥、连接池管理多线程协作、分阶段任务同步
底层机制基于AQS共享锁基于AQS许可证模型基于AQS条件变量与锁

五、其他

1、Exchanger(交换器)

核心机制

  • 线程间数据交换:提供一个同步点,两个线程可以在此交换数据。
  • 阻塞等待:如果一个线程先到达交换点,它会等待另一个线程到达后才能完成交换。
  • 线程安全:内部实现已做好线程安全处理,无需额外加锁。

核心方法

  • exchange(V x):将当前线程的数据x与对方线程的数据进行交换,并返回对方线程的数据。
  • exchange(V x, long timeout, TimeUnit unit):带有超时参数的交换数据方法,如果在指定时间内没有另一个线程到达交换点,则返回null或抛出异常。

典型场景

  • 生产者-消费者模型:生产者生产一批数据,消费者需要拿到这批数据来处理。使用Exchanger可以让生产者直接把数据交给消费者,而不需要通过队列等中间结构。
  • 大数据分块处理:将数据分成两块,让两个线程分别处理。处理完一块后,两个线程可以用Exchanger交换数据,比如交换处理结果或者中间状态。
  • 双人协作模拟:在模拟系统中,可能需要两个线程互相同步,比如模拟两个人在合作完成一项任务时需要交换信息的情况。

示例代码(生产者-消费者模型):

import java.util.concurrent.Exchanger;

public class ExchangerDemo {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();

        // 生产者线程
        new Thread(() -> {
            try {
                String data = "生产的数据";
                System.out.println("生产者生产数据: " + data);
                String received = exchanger.exchange(data);
                System.out.println("生产者收到消费者数据: " + received);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        // 消费者线程
        new Thread(() -> {
            try {
                String data = exchanger.exchange(null); // 初始时消费者没有数据可交换
                System.out.println("消费者收到生产者数据: " + data);
                String response = "处理后的数据";
                exchanger.exchange(response); // 发送处理后的数据给生产者
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
2、Phaser

Phaser 是 Java 并发包(java.util.concurrent)中的一个高级同步工具类,专为协调多个线程分阶段执行任务而设计。 它结合了 CyclicBarrier 和 CountDownLatch 的功能,并提供了动态调整参与者数量、多阶段同步等高级特性,适用于复杂的并发任务流程控制。

核心机制
  • 多阶段同步

    • Phaser 将任务划分为多个阶段(Phase),每个阶段结束时所有线程需同步,之后才能进入下一阶段。
    • 阶段号从 0 开始递增,直至终止。
  • 动态线程管理

    • 线程可通过 register() 动态注册为参与者,或通过 arriveAndDeregister() 注销,无需在初始化时固定线程数量。
    • 内部使用 AtomicLong 存储状态,包含当前阶段号(高 32 位)、已注册参与者数(中 16 位)、未到达参与者数(低 16 位)。
  • 状态推进

    • arriveAndAwaitAdvance():线程完成任务后等待其他参与者到达,全部到达后自动推进阶段。
    • 重写 onAdvance(int phase, int parties) 可自定义阶段结束行为(如数据汇总),返回 true 时 Phaser 终止。
核心特性
  • 灵活性

    • 支持动态增减参与者,适用于线程数量不确定的场景。
    • 支持多阶段同步,每个阶段可以有不同数量的参与者。
  • 可重用性

    • 阶段自动重置,无需手动重置(如 CyclicBarrier)。
  • 分层结构

    • 支持父子 Phaser 树,用于分布式任务协调。
  • 自定义行为

    • 通过 onAdvance() 方法在每个阶段结束时执行自定义逻辑,如日志记录、资源释放等。
使用方法与代码示例
  • 基础用法:三阶段任务同步
import java.util.concurrent.Phaser;

public class PhaserDemo {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(3); // 初始注册3个参与者
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 完成阶段1");
                phaser.arriveAndAwaitAdvance(); // 同步点1
                System.out.println(Thread.currentThread().getName() + " 完成阶段2");
                phaser.arriveAndAwaitAdvance(); // 同步点2
                System.out.println(Thread.currentThread().getName() + " 完成阶段3");
                phaser.arriveAndDeregister(); // 注销并推进
            }).start();
        }
    }
}
  • 动态任务注册:批量数据处理
import java.util.List;
import java.util.concurrent.Phaser;

class BatchProcessor implements Runnable {
    private final Phaser phaser;
    private final List<String> data;

    BatchProcessor(Phaser phaser, List<String> data) {
        this.phaser = phaser;
        this.data = data;
        phaser.register(); // 动态注册新参与者
    }

    @Override
    public void run() {
        while (!data.isEmpty()) {
            processBatch(data.subList(0, Math.min(10, data.size())));
            phaser.arriveAndAwaitAdvance(); // 等待其他批次完成
        }
        phaser.arriveAndDeregister(); // 处理完毕,注销
    }

    private void processBatch(List<String> batch) {
        // 数据处理逻辑
    }
}
  • 自定义阶段回调
import java.util.concurrent.Phaser;

public class CustomPhaserDemo {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(3) {
            @Override
            protected boolean onAdvance(int phase, int parties) {
                System.out.println("阶段 " + phase + " 完成,参与线程数: " + parties);
                return phase >= 2; // 执行3个阶段后终止
            }
        };
        // 创建并启动线程(代码略,与基础用法示例类似)
    }
}
优缺点分析
维度优点缺点
灵活性支持动态增减参与者、多阶段同步学习曲线陡峭,API 复杂于 CyclicBarrier
可扩展性适合大规模并发任务(如千人游戏房间)性能开销:状态维护和 CAS 操作在高并发下可能成为瓶颈
功能强大支持分层、自定义终止条件、超时控制不适用简单场景,单阶段或固定线程数时,CountDownLatch 更简单
应用场景
  • 分治算法:如归并排序,每阶段需子任务全部完成。
  • 爬虫系统:线程根据任务量动态加入/退出。
  • 多玩家回合制游戏:每回合需所有玩家操作完毕。
  • ETL 任务:分批次执行,批次间需同步。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack_abu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值