Java并发开发的几个工具类,助你在并发开发控制上面游刃有余

一、CountDownLatch

CountDownLatch可以理解为一个倒计时计数器,它允许一个或多个线程等待,直到其他线程完成操作。

通俗解释:就像运动会的百米赛跑,所有运动员(线程)在起跑线等待,裁判(主线程)鸣枪(countDown)后,所有运动员才能开始跑。

核心方法:

  • CountDownLatch(int count):构造函数,count为计数器的初始值

  • await():使当前线程等待,直到计数器减到0

  • countDown():计数器减1

示例代码:

public class CountDownLatchDemo {
   
       public static void main(String[] args) throws InterruptedException {
   
           // 模拟5个玩家加载游戏场景        int playerCount = 5;        CountDownLatch latch = new CountDownLatch(playerCount);
        for (int i = 1; i <= playerCount; i++) {
   
               new Thread(() -> {
   
                   try {
   
                       // 模拟加载时间                    Thread.sleep((long) (Math.random() * 3000));                    System.out.println(Thread.currentThread().getName() + " 加载完成");                } catch (InterruptedException e) {
   
                       e.printStackTrace();                } finally {
   
                       latch.countDown(); // 计数器减1                }            }, "玩家-" + i).start();        }
        System.out.println("等待所有玩家加载完成...");        latch.await(); // 主线程等待        System.out.println("所有玩家加载完成,游戏开始!");    }}

实际应用场景

  • 多任务开始前的统一初始化

  • 并行计算,等待所有计算完成

  • 服务启动时等待所有组件加载完成

二、CyclicBarrier

CyclicBarrier让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,所有线程才会继续执行。

通俗解释:就像朋友聚餐,必须所有人都到齐了才能开始吃饭。

与CountDownLatch的区别:

  • CountDownLatch是一次性的,CyclicBarrier可重复使用

  • CountDownLatch主线程等待,CyclicBarrier是所有线程互相等待

  • CyclicBarrier可以在所有线程到达后执行一个回调(Runnable)

示例代码:

public class CyclicBarrierDemo {
   
       public static void main(String[] args) {
   
           // 4个线程互相等待        int threadCount = 4;        CyclicBarrier barrier = new CyclicBarrier(threadCount, () ->             System.out.println("所有线程已就位,开始执行下一步"));
        for (int i = 0; i < threadCount; i++) {
   
               new Thread(new Worker(barrier), "线程-" + (i + 1)).start();        }    }
    static class Worker implements Runnable {
   
           private final CyclicBarrier barrier;
        public Worker(CyclicBarrier barrier) {
   
               this.barrier = barrier;        }
        @Override        public void run() {
   
               try {
   
                   System.out.println(Thread.currentThread().getName() + " 正在执行第一阶段工作");                Thread.sleep((long) (Math.random() * 2000));                System.out.println(Thread.currentThread().getName() + " 到达屏障点,等待其他线程");                barrier.await(); // 等待其他线程
                System.out.println(Thread.currentThread().getName() + " 开始执行第二阶段工作");            } catch (Exception e) {
   
                   e.printStackTrace();            }        }    }}

应用场景:

  • 多阶段任务,每个阶段需要所有线程完成才能进入下一阶段

  • 并行迭代算法

  • 模拟多玩家游戏的回合制场景

三、Semphore

Semaphore用于控制同时访问特定资源的线程数量,通过许可证(permits)机制实现。

通俗解释:就像停车场的车位,总共只有N个车位(permits),当车位满了,其他车必须等待有车离开才能进入。

核心方法:

  • Semaphore(int permits):构造函数,permits为许可证数量

  • acquire():获取许可证,如果没有则阻塞

  • release():释放许可证

  • tryAcquire():尝试获取许可证,不阻塞

示例代码:​​​​​​​

public class SemaphoreDemo {
   
       public static void main(String[] args) {
   
           // 模拟3个打印机        int printerCount = 3;        Semaphore semaphore = new Semaphore(printerCount);
        // 10个打印任务        for (int i = 1; i <= 10; i++) {
   
               new Thread(new PrintTask(semaphore), "任务-" + i).start();        }    }
    static class PrintTask implements Runnable {
   
           private final Semaphore semaphore;
        public PrintTask(Semaphore semaphore) {
   
               this.semaphore = semaphore;        }
        @Override        public void run() {
   
               try {
   
                   System.out.println(Thread.currentThread().getName() + " 等待打印");                semaphore.acquire(); // 获取许可证
                System.out.println(Thread.currentThread().getName() + " 开始打印");                Thread.sleep((long) (Math.random() * 3000)); // 模拟打印时间
                System.out.println(Thread.currentThread().getName() + " 打印完成");                semaphore.release(); // 释放许可证            } catch (InterruptedException e) {
   
                   e.printStackTrace();            }        }    }}

应用场景:

  • 资源池管理(数据库连接池等)

  • 限流控制

  • 生产者-消费者问题

四、Phaser

Phaser是Java 7引入的更灵活的阶段同步器,可以替代CountDownLatch和CyclicBarrier,支持动态调整参与线程数。

通俗解释:就像游戏中的多阶段副本,每个阶段都有不同的玩家参与,可以动态加入或退出。

核心特点:

  • 动态注册/注销参与方

  • 支持分层结构

  • 可重用的同步点

  • 支持终止机制

使用示例:​​​​​​​

public class PhaserDemo {
   
       public static void main(String[] args) {
   
           // 创建Phaser,初始参与方为0        Phaser phaser = new Phaser() {
   
               // 可选的onAdvance方法,在阶段推进前调用            @Override            protected boolean onAdvance(int phase, int registeredParties) {
   
                   System.out.println("======= 阶段" + phase + "完成 =======");                return registeredParties == 0; // 返回true表示终止            }        };
        // 创建并启动5个任务        for (int i = 0; i < 5; i++) {
   
               // 注册新的参与方            phaser.register();            new Thread(new Task(phaser), "线程-" + (i + 1)).start();        }    }
    static class Task implements Runnable {
   
           private final Phaser phaser;
        public Task(Phaser phaser) {
   
               this.phaser = phaser;        }
        @Override        public void run() {
   
               // 阶段0            System.out.println(Thread.currentThread().getName() + " 执行阶段0工作");            phaser.arriveAndAwaitAdvance(); // 等待其他线程
            // 阶段1            System.out.println(Thread.currentThread().getName() + " 执行阶段1工作");            phaser.arriveAndAwaitAdvance();
            // 阶段2            System.out.println(Thread.currentThread().getName() + " 执行阶段2工作");            phaser.arriveAndDeregister(); // 完成并注销        }    }}

应用场景 :

  • 复杂多阶段任务处理

  • 动态调整参与线程数的场景

五、Exchanger

Exchanger用于两个线程间交换数据,当一个线程调用exchange()方法时,它会等待另一个线程也调用exchange()方法。

通俗解释:就像两个间谍在交换情报,必须两个人都到场才能交换。

示例代码:​​​​​​

public class ExchangerDemo {
   
       public static void main(String[] args) {
   
           Exchanger<String> exchanger = new Exchanger<>();
        new Thread(() -> {
   
               try {
   
                   String data = "数据A";                System.out.println("线程1准备交换: " + data);                String received = exchanger.exchange(data);                System.out.println("线程1收到: " + received);            } catch (InterruptedException e) {
   
                   e.printStackTrace();            }        }).start();
        new Thread(() -> {
   
               try {
   
                   String data = "数据B";                System.out.println("线程2准备交换: " + data);                String received = exchanger.exchange(data);                System.out.println("线程2收到: " + received);            } catch (InterruptedException e) {
   
                   e.printStackTrace();            }        }).start();    }}

应用场景 

  • 两个线程间的数据交换

  • 管道替代方案

  • 校对计算结果

六、总结

  1. CountDownLatch:一次性使用,一个或多个线程等待其他线程完成

  2. CyclicBarrier:可重复使用,所有线程互相等待

  3. Semaphore:控制资源访问数量

  4. Phaser:灵活的多阶段同步,支持动态调整

  5. Exchanger:两个线程间交换数据