CountDownLatch

CountDownLatch适用于在多线程的场景需要守候所有子线程所有执行完毕之后再做操作的场景。

举个例子,早上部门开会,有人在上茅厕,这时刻需要守候所有人从茅厕回来之后才气最先集会。

public class CountDownLatchTest {
   private static int num = 3;
   private static CountDownLatch countDownLatch = new CountDownLatch(num);
   private static ExecutorService executorService = Executors.newFixedThreadPool(num);
   public static void main(String[] args) throws Exception{
       executorService.submit(() -> {
           System.out.println("A在上茅厕");
           try {
               Thread.sleep(4000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }finally {
               countDownLatch.countDown();
               System.out.println("A上完了");
           }
       });
       executorService.submit(()->{
           System.out.println("B在上茅厕");
           try {
               Thread.sleep(2000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }finally {
               countDownLatch.countDown();
               System.out.println("B上完了");
           }
       });
       executorService.submit(()->{
           System.out.println("C在上茅厕");
           try {
               Thread.sleep(3000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }finally {
               countDownLatch.countDown();
               System.out.println("C上完了");
           }
       });

       System.out.println("守候所有人从茅厕回来开会...");
       countDownLatch.await();
       System.out.println("所有人都好了,最先开会...");
       executorService.shutdown();

   }}

代码执行效果:

A在上茅厕
B在上茅厕
守候所有人从茅厕回来开会...C在上茅厕
B上完了
C上完了
A上完了
所有人都好了,最先开会...

初始化一个CountDownLatch实例传参3,由于我们有3个子线程,每次子线程执行完毕之后挪用countDown()方式给计数器-1,主线程挪用await()方式后会被壅闭,直到最后计数器变为0,await()方式返回,执行完毕。他和join()方式的区别就是join会壅闭子线程直到运行竣事,而CountDownLatch可以在任何时刻让await()返回,而且用ExecutorService没法用join了,相比起来,CountDownLatch更天真。

CountDownLatch基于AQS实现,volatile变量state维持倒数状态,多线程共享变量可见。

  1. CountDownLatch通过组织函数初始化传入参数现实为AQS的state变量赋值,维持计数器倒数状态

  2. 当主线程挪用await()方式时,当前线程会被壅闭,当state不为0时进入AQS壅闭行列守候。

  3. 其他线程挪用countDown()时,state值原子性递减,当state值为0的时刻,叫醒所有挪用await()方式壅闭的线程

CyclicBarrier

CyclicBarrier叫做回环屏障,它的作用是让一组线程所有到达一个状态之后再所有同时执行,而且他有一个特点就是所有线程执行完毕之后是可以重用的。

public class CyclicBarrierTest {
   private static int num = 3;
   private static CyclicBarrier cyclicBarrier = new CyclicBarrier(num, () -> {
       System.out.println("所有人都好了,最先开会...");
       System.out.println("-------------------");
   });
   private static ExecutorService executorService = Executors.newFixedThreadPool(num);
   public static void main(String[] args) throws Exception{
       executorService.submit(() -> {
           System.out.println("A在上茅厕");
           try {
               Thread.sleep(4000);
               System.out.println("A上完了");
               cyclicBarrier.await();
               System.out.println("集会竣事,A退出");
           } catch (Exception e) {
               e.printStackTrace();
           }finally {

           }
       });
       executorService.submit(()->{
           System.out.println("B在上茅厕");
           try {
               Thread.sleep(2000);
               System.out.println("B上完了");
               cyclicBarrier.await();
               System.out.println("集会竣事,B退出");
           } catch (Exception e) {
               e.printStackTrace();
           }finally {

           }
       });
       executorService.submit(()->{
           System.out.println("C在上茅厕");
           try {
               Thread.sleep(3000);
               System.out.println("C上完了");
               cyclicBarrier.await();
               System.out.println("集会竣事,C退出");
           } catch (Exception e) {
               e.printStackTrace();
           }finally {

           }
       });

       executorService.shutdown();

   }}

输出效果为:

A在上茅厕
B在上茅厕
C在上茅厕
B上完了
C上完了
A上完了
所有人都好了,最先开会...-------------------集会竣事,A退出
集会竣事,B退出
集会竣事,C退出

从效果来看和CountDownLatch异常相似,初始化传入3个线程和一个义务,线程挪用await()之后进入壅闭,计数器-1,当计数器为0时,就去执行CyclicBarrier中组织函数的义务,当义务执行完毕后,叫醒所有壅闭中的线程。这验证了CyclicBarrier让一组线程所有到达一个状态之后再所有同时执行的效果。

再举个例子来验证CyclicBarrier可重用的效果。

public class CyclicBarrierTest2 {
   private static int num = 3;
   private static CyclicBarrier cyclicBarrier = new CyclicBarrier(num, () -> {
       System.out.println("-------------------");
   });
   private static ExecutorService executorService = Executors.newFixedThreadPool(num);

   public static void main(String[] args) throws Exception {
       executorService.submit(() -> {
           System.out.println("A在上茅厕");
           try {
               Thread.sleep(4000);
               System.out.println("A上完了");
               cyclicBarrier.await();
               System.out.println("集会竣事,A退出,最先撸代码");
               cyclicBarrier.await();
               System.out.println("C事情竣事,下班回家");
               cyclicBarrier.await();
           } catch (Exception e) {
               e.printStackTrace();
           } finally {

           }
       });
       executorService.submit(() -> {
           System.out.println("B在上茅厕");
           try {
               Thread.sleep(2000);
               System.out.println("B上完了");
               cyclicBarrier.await();
               System.out.println("集会竣事,B退出,最先摸鱼");
               cyclicBarrier.await();
               System.out.println("B摸鱼竣事,下班回家");
               cyclicBarrier.await();
           } catch (Exception e) {
               e.printStackTrace();
           } finally {

           }
       });
       executorService.submit(() -> {
           System.out.println("C在上茅厕");
           try {
               Thread.sleep(3000);
               System.out.println("C上完了");
               cyclicBarrier.await();
               System.out.println("集会竣事,C退出,最先摸鱼");
               cyclicBarrier.await();
               System.out.println("C摸鱼竣事,下班回家");
               cyclicBarrier.await();
           } catch (Exception e) {
               e.printStackTrace();
           } finally {

           }
       });

       executorService.shutdown();

   }}

输出效果:

A在上茅厕
B在上茅厕
C在上茅厕
B上完了
C上完了
A上完了-------------------集会竣事,A退出,最先撸代码
集会竣事,B退出,最先摸鱼
集会竣事,C退出,最先摸鱼-------------------C摸鱼竣事,下班回家
C事情竣事,下班回家
B摸鱼竣事,下班回家-------------------

从效果来看,每个子线程挪用await()计数器减为0之后才最先继续一起往下执行,集会竣事之后一起进入摸鱼状态,最后一天竣事一起下班,这就是可重用。

CyclicBarrier照样基于AQS实现的,内部维护parties纪录总线程数,count用于计数,最最先count=parties,挪用await()之后count原子递减,当count为0之后,再次将parties赋值给count,这就是复用的原理。

  1. 当子线程挪用await()方式时,获取独占锁,同时对count递减,进入壅闭行列,然后释放锁

    ,

    欧博开户

    欢迎进入欧博开户(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

    ,
  2. 当第一个线程被壅闭同时释放锁之后,其他子线程竞争获取锁,操作同1

  3. 直到最后count为0,执行CyclicBarrier组织函数中的义务,执行完毕之后子线程继续向下执行

Semaphore

Semaphore叫做信号量,和前面两个差别的是,他的计数器是递增的。

public class SemaphoreTest {
   private static int num = 3;
   private static int initNum = 0;
   private static Semaphore semaphore = new Semaphore(initNum);
   private static ExecutorService executorService = Executors.newFixedThreadPool(num);
   public static void main(String[] args) throws Exception{
       executorService.submit(() -> {
           System.out.println("A在上茅厕");
           try {
               Thread.sleep(4000);
               semaphore.release();
               System.out.println("A上完了");
           } catch (Exception e) {
               e.printStackTrace();
           }finally {

           }
       });
       executorService.submit(()->{
           System.out.println("B在上茅厕");
           try {
               Thread.sleep(2000);
               semaphore.release();
               System.out.println("B上完了");
           } catch (Exception e) {
               e.printStackTrace();
           }finally {

           }
       });
       executorService.submit(()->{
           System.out.println("C在上茅厕");
           try {
               Thread.sleep(3000);
               semaphore.release();
               System.out.println("C上完了");
           } catch (Exception e) {
               e.printStackTrace();
           }finally {

           }
       });

       System.out.println("守候所有人从茅厕回来开会...");
       semaphore.acquire(num);
       System.out.println("所有人都好了,最先开会...");

       executorService.shutdown();

   }}

输出效果为:

A在上茅厕
B在上茅厕
守候所有人从茅厕回来开会...C在上茅厕
B上完了
C上完了
A上完了
所有人都好了,最先开会...

稍微和前两个有点区别,组织函数传入的初始值为0,当子线程挪用release()方式时,计数器递增,主线程acquire()传参为3则说明主线程一直壅闭,直到计数器为3才会返回。

Semaphore还还照样基于AQS实现的,同时获取信号量有公正和非公正两种计谋

  1. 主线程挪用acquire()方式时,用当前信号量值-需要获取的值,若是小于0,则进入同步壅闭行列,大于0则通过CAS设置当前信号量为剩余值,同时返回剩余值

  2. 子线程挪用release()给当前信号量值计数器+1(增添的值数目由传参决议),同时一直的实验由于挪用acquire()进入壅闭的线程

总结

CountDownLatch通过计数器提供了比join更天真的多线程控制方式,CyclicBarrier也可以到达CountDownLatch的效果,而且有可复用的特点,Semaphore则是接纳信号量递增的方式,最先的时刻并不需要关注需要同步的线程个数,而且提供获取信号的公正和非公正计谋。

最后

谢谢人人看到这里,文章有不足,迎接人人指出;若是你以为写得不错,那就给我一个赞吧。