> 文章列表 > JUC之CountDownLatch与CyclicBarrier

JUC之CountDownLatch与CyclicBarrier

JUC之CountDownLatch与CyclicBarrier

1.前言

java.util.concurrent包中为我们提供了很多的线程同步工具类,例如CountDownLatchCyclicBarrier,那么它们主要的用途是什么呢?且看后续分析。

2.CountDownLatch

2.1 什么是CountDownLatch

CountDownLatch,顾名思义,这是一个带计数器的线程同步工具。我们来看官方解释:

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

意味着:CountDownLatch是一个线程同步工具,它允许一个或多个线程进入等待状态,直到其他线程执行完毕后,这些等待的线程才继续往下执行。

这个解释或许对于没有接触过老铁还比较生涩,不方,我们还有实战环节。

2.2 CountDownLatch使用场景

其实,在我们的生活中是有很多场景可以运用CountDownLatch来进行实现的。

这是阳光明媚的一天,部门小伙伴相约春游,滴滴师傅已经达到上车点了,就等各位小伙伴全部上车后,就可以出发了。怎么样,这个场景是不是和CountDownLatch的概念很契合呀,但是我们先用Thread.join()的方式来实现,然后才是我们的CountDownLatch,接下来我们通过代码来实现吧。
JUC之CountDownLatch与CyclicBarrier
1)Thread.join()实现

主要思路:

1)一个滴滴师傅线程,正在等待所有小伙伴上车,上车后继续运行

2)五个小伙伴线程,相继上车

代码示例:

public class CountDownLatchTest {@SneakyThrowspublic static void main(String[] args) {DriverThread threadDriver = new DriverThread();Thread threadA = new Thread(() -> System.out.println(Thread.currentThread().getName() + "已上车"), "张三");Thread threadB = new Thread(() -> System.out.println(Thread.currentThread().getName() + "已上车"), "张四");Thread threadC = new Thread(() -> System.out.println(Thread.currentThread().getName() + "已上车"), "张五");Thread threadD = new Thread(() -> System.out.println(Thread.currentThread().getName() + "已上车"), "张六");Thread threadE = new Thread(() -> System.out.println(Thread.currentThread().getName() + "已上车"), "张七");threadDriver.setThreads(List.of(threadA, threadB, threadC, threadD, threadE));threadDriver.start();threadA.start();threadB.start();threadC.start();threadD.start();threadE.start();}static class DriverThread extends Thread {private List<Thread> threads;public List<Thread> getThreads() {return threads;}public void setThreads(List<Thread> threads) {this.threads = threads;}@SneakyThrows@Overridepublic void run() {System.out.println("滴滴师傅已经达到上车点,请各位小伙伴尽快上车哟");for (Thread thread : getThreads()) {thread.join();}System.out.println("所有小伙伴全部上车,向着目的地进发");}}
}

执行结果:

JUC之CountDownLatch与CyclicBarrier

我们使用Thread.join()的方式,实现了需求,不过大家有没有发现,这种方式其实是相当的麻烦,一个线程需要等待其他的若干个线程执行完毕才能继续往下执行,这个线程就需要持有这些线程的实例,并调用join()方法。那么有没有更优雅的方式呢?

接下来轮到主角CountDownLatch登场了,话不多说直接开干。

代码示例:

public class CountDownLatchTest {@SneakyThrowspublic static void main(String[] args) {// 5个小伙伴相约春游// 构造计数器为5的CountDownLatch,因为我们有5个小伙伴CountDownLatch countDownLatch = new CountDownLatch(5);new DriverThread(countDownLatch).start();CustomRunnable customRunnable = new CustomRunnable(countDownLatch);new Thread(customRunnable, "张三").start();new Thread(customRunnable, "张四").start();new Thread(customRunnable, "张五").start();new Thread(customRunnable, "张六").start();new Thread(customRunnable, "张七").start();}static class DriverThread extends Thread {private CountDownLatch countDownLatch;public DriverThread(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@SneakyThrows@Overridepublic void run() {System.out.println("滴滴师傅已经达到上车点,请各位小伙伴尽快上车哟");// 使当前线程进入等待状态,直到count = 0 时,唤醒此线程countDownLatch.await();System.out.println("所有小伙伴全部上车,向着目的地进发");}}static class CustomRunnable implements Runnable {private CountDownLatch countDownLatch;public CustomRunnable(CountDownLatch countDownLatch) {this.countDownLatch = countDownLatch;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "已上车");// 将计数器减1,直到count = 0,等待的线程就会继续执行countDownLatch.countDown();}}
}

执行结果:

JUC之CountDownLatch与CyclicBarrier

确实,整个代码会简洁许多。这就是CountDownLatch的用法啦。

接下来我们来分析一下实现代码:

1)CountDownLatch构造方法

创建CountDownLatch,并为计数器设置一个初始值。

public CountDownLatch(int count) {}

2) CountDownLatch.countDown()

目的就是让计数器的数值减1。

public void countDown() {sync.releaseShared(1);
}

3)CountDownLatch.await()

使得当前线程进入等待状态,直到计数值为0,该线程才会继续向下执行

public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}

3.CyclicBarrier

3.1 什么CyclicBarrier

CyclicBarrier:直译的意思是循环屏障,听上去意思是可以当成一个屏障,并且可以重复使用。来看官方的解释:

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

这意味着,CyclicBarrier是一个线程同步工具,它允许一组线程彼此等待,直到这组线程全部执行完毕。

3.2 CyclicBarrier使用场景

这么看上去,这个概念很契合我们的赛跑比赛啊,运动员在起跑线前等待所有运动员准备完毕后,比赛才会正式开始。

JUC之CountDownLatch与CyclicBarrier

接下来我们开始模拟这个场景:

public class CyclicBarrierTest {public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println("所有运动员准备完毕,发令员使用发令枪发出信号,比赛开始。"));CustomRunnable customRunnable = new CustomRunnable(cyclicBarrier);new Thread(customRunnable, "张三").start();new Thread(customRunnable, "张四").start();new Thread(customRunnable, "张五").start();new Thread(customRunnable, "张六").start();new Thread(customRunnable, "张七").start();}static class CustomRunnable implements Runnable {private CyclicBarrier cyclicBarrier;public CustomRunnable(CyclicBarrier cyclicBarrier) {this.cyclicBarrier = cyclicBarrier;}@SneakyThrows@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "准备完毕");// 在屏障前等待cyclicBarrier.await();// 所有线程都达到屏障后,继续执行System.out.println(Thread.currentThread().getName() + "跨越起跑线");}}
}

执行结果:

JUC之CountDownLatch与CyclicBarrier

使用CyclicBarrier,可以很方便的模拟赛跑比赛的场景。

接下来我们分析一下代码实现:

1)构造方法

CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println("所有运动员准备完毕,发令员使用发令枪发出信号,比赛开始。"));

可以结合我们的CyclicBarrier为我们提供的构造方法

 // parties:表示参与的线程个数,barrierAction:表示所有参与线程都达到屏障时,要执行的命令
public CyclicBarrier(int parties, Runnable barrierAction) {}

2)CyclicBarrier.await();

使得先到的线程在屏障前等待,直到所有线程都达到屏障

cyclicBarrier.await();

3)CyclicBarrier.reset();

使得计数值重置为初始值,这也是为什么叫循环屏障的原因。

4.CountDownLatch和CyclicBarrier的区别

相信大家已经对CountDownLatchCyclicBarrier有所了解,一起来总结一下它们的区别:

  • CountDownLatch是一个线程或多个线程进行等待其他线程执行完毕,CyclicBarrier是线程之间彼此等待。
  • CountDownLatchawait()方法由需要进入等待状态的线程调用,CyclicBarrierawait()方法由所有参与线程调用
  • CountDownLatch的计数值为0后不可重置,CyclicBarrier的计数值为0后可以重置。