在 Java 并发开发中,除了 synchronized、Lock 等互斥锁之外,还有三个非常常用的线程协作工具:
- 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:一起继续
评论
填写昵称与邮箱即可评论,无需登录。