CountDownLatch
CountDownLatch
的基本使用
CountDownLatch
通过一个计数器来跟踪某些操作的完成次数 。线程会被阻塞 ,直到计数器变为零 。一般来说,CountDownLatch
用于以下场景:
- 等待多个线程执行完成:例如,等待多个子线程完成任务后,主线程再继续执行。
- 并行任务的同步:确保一些任务在所有子任务完成之后才继续执行。
csharp
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "执行完毕");
countDownLatch.countDown();
}, "t" + i).start();
}
System.out.println("等待所有线程执行完毕");
}
}

CountDownLatch
****的关键方法
await()
:使当前线程等待 ,直到计数器的值为零。该方法是阻塞的,线程会在此方法处被挂起,直到计数器的值减为 0。

CountDownLatch
的应用场景
- 等待多个线程完成任务:
-
- 比如,在一个任务分解为多个子任务的场景中,我们希望等所有的子任务完成后,再继续进行下一个步骤。
CountDownLatch
就可以用来保证主线程等到所有子线程的任务完成后再继续执行。
- 比如,在一个任务分解为多个子任务的场景中,我们希望等所有的子任务完成后,再继续进行下一个步骤。
- 并行任务的同步控制:
-
- 比如,某些操作必须等到多个任务执行完成后才能继续,
CountDownLatch
可以确保这些任务完成后才继续处理。
- 比如,某些操作必须等到多个任务执行完成后才能继续,
- 启动多个线程并等待其完成:
-
- 比如,在并行处理的情况下,启动多个线程并在主线程中等待所有线程的完成,可以通过
CountDownLatch
来实现。
- 比如,在并行处理的情况下,启动多个线程并在主线程中等待所有线程的完成,可以通过
总之就是在某些场景下,必须等待一些线程完成后才能继续进行就可以使用这个 CountDownLatch
****
CyclicBarrier
CyclicBarrier
的基本原理
CyclicBarrier
使得多个线程在执行某个任务时,必须等待其他线程到达屏障点。每当所有线程都到达屏障点时,CyclicBarrier
会让这些线程继续执行。可以理解为它提供了一种线程间的协作方式,多个线程在到达同步点时会被阻塞,直到所有线程都到达这个点。
与 CountDownLatch
不同,CyclicBarrier
是 可重用 的。CountDownLatch
一旦计数器变为零后就不能重用,而 CyclicBarrier
在每次屏障点通过后,可以重置为初始状态,允许进行下一轮的等待。
CyclicBarrier
的关键方法
await()
:每个线程调用此方法,表示该线程已经到达了屏障点并且等待其他线程的到达。调用 await()
方法的线程会被阻塞,直到所有线程都到达屏障点。
csharp
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("集齐7颗龙珠,召唤神龙");
});
// 创建7个线程模拟收集龙珠
for (int i = 1; i <= 7; i++) {
final int tempInt = i;
new Thread(() -> {
try {
System.out.println("收集到第" + tempInt + "颗龙珠");
// 每个线程到达屏障时调用await
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
CyclicBarrier
的应用场景
- 并行任务的同步 :比如你有多个线程并行执行任务,每个线程执行完任务后都需要等待其他线程完成某些操作再继续,
CyclicBarrier
可以用来在这些线程之间同步操作。 - 分批执行的任务:多个任务被分成多个子任务并行处理,每次等待所有子任务完成后再进行下一轮操作。
- 分阶段执行 :比如某个计算任务分为多个阶段,每个阶段都需要多个线程协作,
CyclicBarrier
可以确保每个阶段的任务都在所有线程完成后同步进行
在这种情况下会发生死锁

总结
CyclicBarrier
是一个同步工具类,允许多个线程在执行任务时相互等待,直到所有线程都到达屏障点后才继续执行。- 它是 可重用 的,并且适用于需要线程间在多个阶段同步执行的场景。
- 使用
CyclicBarrier
时需要注意可能的死锁问题,以及适当处理线程中断和屏障破坏的异常。
Semaphore
Semaphore
的基本原理
Semaphore
维护了一个许可证计数器。每当一个线程请求资源时,它尝试从 Semaphore
中获取一个许可证,如果计数器大于 0,则获取一个许可证并将计数器减 1;否则线程会被阻塞,直到有许可证可用。释放资源时,线程释放许可证,计数器加 1,其他等待的线程可以继续执行。
Semaphore
的关键方法
acquire()
:请求一个许可证。如果没有可用的许可证,当前线程会阻塞直到有许可证可用。
release()
:释放一个许可证,使得其他等待的线程能够获得许可。如果有线程在等待许可证,release()
会唤醒其中一个线程。
Semaphore
应用场景
- 限制并发线程数:通过限制某个资源的并发访问数量。例如,限制数据库连接池中最大连接数,或者限制网络带宽的并发访问。
- 访问控制:控制对某些资源(如文件、设备等)的并发访问,确保同时访问资源的线程数不会超过设定的阈值。
- 限流控制 :在 Web 服务或 API 中,使用
Semaphore
可以实现对请求的并发数限制,从而防止服务器被过多请求压垮。
总结
一个加法计数器 一个减法计数器 一个信号量
特性 | Semaphore |
CountDownLatch |
CyclicBarrier |
---|---|---|---|
主要功能 | 控制并发线程数,管理资源池 | 等待多个线程执行完毕,或者多个线程完成后继续执行 | 等待多个线程到达屏障点后继续执行 |
可重用性 | 可重用:允许多次获取和释放许可证 | 不可重用:计数器归零后不能重置 | 可重用:每次所有线程到达屏障后,可以重用 |
适用场景 | 控制并发访问共享资源 | 等待多个线程完成后统一执行某些操作 | 多阶段任务的同步,多个线程在不同阶段同步 |
阻塞与唤醒 | 线程通过 acquire() 获取许可,release() 释放许可 |
线程通过 await() 等待,countDown() 减少计数器 |
线程通过 await() 等待,所有线程到达后继续执行 |
常见用途 | 资源池管理,流量控制,限流 | 并行任务同步,启动多个线程后等待它们完成 | 分阶段同步,线程池等同步任务 |