CountDownLatch
CountDownLatch,闭锁,是一个同步工具类,用来协调多个线程之间的同步。
CountDownLatch是Java并发编程中的一把利器,它的优点在于能够简洁高效地协调多个线程的执行顺序,确保一组线程都完成后才触发其他线程的执行,适用于资源加载、任务初始化等场景。它提供了清晰的等待/通知机制,易于理解和使用,是提升多线程程序性能和可靠性的重要工具,但同时,它也有一些局限性,比如无法重置计数,一旦计数到零,就不能再次使用了。而且,它只能等待固定数量的线程,不够灵活。
简单来说,当一个或者多个线程调用await方法时,会阻塞当前线程,并且CountDownLatch本身内置了一个方法计数器,其他线程调用 countDown 方法使计数器 - 1,当计数器的值变为0时,调用 await 方法被阻塞的线程会被唤醒,继续执行。
不使用CountDownLatch
因为线程是竞争执行的,如果不使用CountDownLatch的话, 无法保证主线程最后执行
public static void main(String[] args) {for (int i = 0; i < 5; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "到达指定地点");}).start();}System.out.println(Thread.currentThread().getName() + "五个同学都到齐了!");}
使用CountDownLatch
可以看到,主线程最后才执行
public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(5);for (int i = 0; i < 5; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "到达指定地点");countDownLatch.countDown();}).start();}// 主线程在此等待,直到所有工作线程都完成任务 countDownLatch.await();System.out.println(Thread.currentThread().getName() + "五个同学都到齐了!");}
CyclicBarrier
CyclicBarrier,循环屏障,它支持一组线程相互等待,直到最后一个线程到达屏障点时,所有线程才会继续执行。CyclicBarrier可以被重用,即当所有等待的线程都被释放后,它可以再次被用来让另一组线程等待。
例子
宿舍五人一起去食堂干饭,要等大家都回来再出发,人齐后大家都开炫
public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {System.out.println("人齐了,集合去干饭!");});for (int i = 0; i < 5; i++) {int finalI = i;new Thread(() -> {System.out.println("老" + finalI + "到了集合地点");try {cyclicBarrier.await();System.out.println("老" + finalI + "开炫");} catch (InterruptedException | BrokenBarrierException e) {throw new RuntimeException(e);}}).start();}}
执行结果如下
可以看到使用 CountDownLatch ,调用 countDown方法之后,之后的代码仍会继续执行,CountDownLatch 只能保证主线程或者调用await方法最后执行,其他调用countDown方法的线程早就执行结束了。
而 CyclicBarrier 的 await() 方法会阻塞,等所有线程执行结束后,所有调用 await() 线程会一起执行。
相同点
- 同步功能:
- 两者都提供了等待多个线程完成特定任务的能力。
- 它们都可以用来协调一组线程之间的执行顺序。
- 等待机制:
- 在使用这两种工具时,某些线程会阻塞,直到满足某个条件(比如计数器达到零或所有线程到达屏障)。
- 线程等待:
- 当线程调用await()方法时,它们都会进入等待状态,直到满足释放条件。
- 灵活性:
- 它们都允许在等待期间执行某些操作,如中断等待的线程。
- 异常处理:
- 如果在等待过程中发生异常,两个工具类都能通过异常传播机制来处理这些异常。
不同点
- CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
- CountDownLatch 的计数器只能使用一次,而CyclicBarrier 的计数器可以使用 reset方法重置
- CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。