跳到正文
hello world

Semaphore、CountDownLatch、CyclicBarrier

在 Java 并发开发中,除了 synchronizedLock 等互斥锁之外,还有三个非常常用的线程协作工具:

  • Semaphore(信号量)
  • CountDownLatch(倒计数器)
  • CyclicBarrier(循环栅栏)

它们都属于 java.util.concurrent 包,但解决的问题完全不同。


一、Semaphore(信号量)

作用

控制同时访问某个资源的线程数量

可以把它理解成一个门卫,只有拿到许可证(Permit)的线程才能进入,许可证用完后,其余线程只能等待。

一句话:控制并发数量。

使用场景

  • 数据库连接池
  • Redis 连接池
  • 外部接口并发限制
  • 文件下载并发控制

示例:同时最多允许三个线程执行

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {

    private static final Semaphore semaphore = new Semaphore(3);

    public static void main(String[] args) {

        for (int i = 1; i <= 10; i++) {
            final int num = i;

            new Thread(() -> {
                try {
                    semaphore.acquire();

                    System.out.println("线程" + num + "开始执行");
                    Thread.sleep(2000);
                    System.out.println("线程" + num + "执行结束");

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}

运行过程中,无论启动多少线程,同时真正执行的始终只有 3 个。


二、CountDownLatch(倒计数器)

作用

一个或多个线程等待其他线程执行完成

它内部维护一个计数器,每完成一个任务就减一,当计数变成 0 时,等待线程继续执行。

一句话:主线程等子线程。

使用场景

  • Spring Boot 初始化
  • 多线程加载缓存
  • 多线程统计结果
  • 并行任务汇总

示例:主线程等待三个子线程完成

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {

    public static void main(String[] args) throws Exception {

        CountDownLatch latch = new CountDownLatch(3);

        for (int i = 1; i <= 3; i++) {

            final int num = i;

            new Thread(() -> {

                System.out.println("线程" + num + "开始工作");

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ignored) {
                }

                System.out.println("线程" + num + "工作完成");

                latch.countDown();

            }).start();
        }

        System.out.println("主线程等待...");

        latch.await();

        System.out.println("所有线程执行完成,主线程继续");
    }
}

这里真正等待的是主线程,而三个工作线程完成任务后并不会互相等待。


三、CyclicBarrier(循环栅栏)

作用

一组线程互相等待,直到所有线程都到达同一个位置,再一起继续执行。

一句话:大家一起出发。

使用场景

  • 分阶段并行计算
  • 多线程数据处理
  • 游戏等待所有玩家准备
  • 多线程批量导入

示例:三个人一起爬山

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {

    public static void main(String[] args) {

        CyclicBarrier barrier = new CyclicBarrier(3,
                () -> System.out.println("所有人到齐,继续前进"));

        for (int i = 1; i <= 3; i++) {

            final int num = i;

            new Thread(() -> {

                try {

                    System.out.println("队员" + num + "开始出发");

                    Thread.sleep(num * 1000);

                    System.out.println("队员" + num + "到达集合点");

                    barrier.await();

                    System.out.println("队员" + num + "继续前进");

                } catch (Exception e) {
                    e.printStackTrace();
                }

            }).start();
        }
    }
}

谁先到集合点,谁就等待;直到三个人全部到达后,再一起继续前进。


三者区别

工具 作用 谁等待谁 是否可重复使用
Semaphore 控制并发数量 等待许可证
CountDownLatch 等待任务完成 主线程等待子线程
CyclicBarrier 等待所有线程到达屏障 所有线程互相等待

总结

如果把三者比作生活中的场景:

  • Semaphore:厕所只有 3 个坑位,同时最多进去 3 个人。
  • CountDownLatch:老师等待所有学生交卷后才能收卷。
  • CyclicBarrier:几个人约好一起爬山,每到一个集合点,都必须等所有人到齐再继续出发。

真正理解它们的关键,不是记 API,而是记住它们分别解决了三类不同的线程协作问题:

  • Semaphore:控制数量
  • CountDownLatch:等待完成
  • CyclicBarrier:一起继续

评论

填写昵称与邮箱即可评论,无需登录。

推荐阅读