《Java后端知识体系》系列之CountDownlatch的原理剖析

知识没有狗粮来的直接,所以先上狗粮!

在这里插入图片描述

线程同步器

  • CountDownlatch原理刨析
    • 场景:
      • 在日常开发中会遇到需要在主线程中开启多个线程去并行执行任务,并且主线程需要等待所有的子线程执行完毕后再进行汇总的场景,对于这种情况在CountDownLatch之前一般使用线程的join()方法来实现此需求,但是join方法不够灵活,不能满足场景需要,因此有了CountDownLatch。

      • 实现方式如下:

        public class CountDownLatch2 {
            private static volatile CountDownLatch countDownLatch = new CountDownLatch(2);
        
            public static void main(String[] args) throws InterruptedException {
                ExecutorService executorService = Executors.newFixedThreadPool(2);
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            countDownLatch.countDown();
                        }
                        System.out.println("child thread over");
                    }
                });
                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }finally {
                            countDownLatch.countDown();
                        }
                        System.out.println("child thread over");
                    }
                });
        
                countDownLatch.await();
                System.out.println("all child over");
                executorService.shutdown();
            }
        }
        

        以上代码中直接通过线程池创建了两个线程,分别沉睡一秒,并且在执行结束会调用countDown方法,在最后调用await方法等待所有的子线程执行完毕才算结束。
        CountDownLatch与join的区别是调用子线程的join方法,该线程会一直被阻塞直到子线程执行完毕,而CountDownLatch则使用计数器来允许子线程运行完毕然后计数器减1,因此CountDownLatch可以在子线程运行时让await方法返回而不需要等到线程结束。

    • 原理: CountDownLatch内部有一个计数器,并且这个计数器是递减的。

      在这里插入图片描述

      从类图中可以看到CountDownLatch是使用AQS来实现的,CountDownLatch内部的计数器的值是相当于AQS的state,也就是通过AQS的state值来表示计数器。

      • void await()

        • 当线程调用await方法后,当前线程会被阻塞,接触阻塞的方式有两种:

          • 只有当所有的线程都调用了CountDownLatch的countDown方法后(也就是计数器的值变为0时)
          • 其它线程调用interrupt()方法中断了当前线程后才会释放
              public void await() throws InterruptedException {
                  sync.acquireSharedInterruptibly(1);
              }
          	//AQS中的方法
              public final void acquireSharedInterruptibly(int arg)
                      throws InterruptedException {
                   //如果线程被中断则抛出异常
                  if (Thread.interrupted())
                      throw new InterruptedException();
                   //查看当前计数器的值是否为0,为0则直接返回,否则进入AQS的队列等待
                  if (tryAcquireShared(arg) < 0)
                      doAcquireSharedInterruptibly(arg);
              }
          

        该方法中在线程获取资源时是可以被中断的,并且获取的资源是共享资源,acquireSharedInterruptibly方法中首先判断线程是否被中断,如果被中断则抛出异常否则调用tryAcquireShared方法查看当前状态值是否为0,为0则直接返回,否则让当前线程阻塞放入AQS队列中。

      • boolean await(long timeout, TimeUnit unit )

        该方法中跟上一个方法效果一样,但是这个方法有时间参数,也就是如果该线程超过了设定的时间,则会超时然后抛出false。

      • void countDown()

            public void countDown() {
            	//调用sync的releaseShared方法
                sync.releaseShared(1);
            }
            public final boolean releaseShared(int arg) {
        	     if (tryReleaseShared(arg)) {
        	     	//AQS释放资源的方法
        	         doReleaseShared();
        	         return true;
        	     }
        	     return false;
        	 }
        	protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            //循环进行CAS,直到当前线程成功完成CAS使计数器减1并更新到state
            for (;;) {
                int c = getState();
                //如果当前状态值为0则直接返回
                if (c == 0)
                    return false;
                 //使用CAS让计数器减1
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
        

        在如上代码中首先获取到状态值,然后判断状态值是否为0,如果为0则直接返回,否则通过CAS将计数器递减1,如果CAS失败则循环重试。

    • 总结:

      CountDownLatch相比于join方法来实现线程同步来说更具有灵活性。另外CountDownLatch的实现也是基于AQS,CountDownLatch中的计数器的值则是通过AQS中的state值来维护的,当计数器变为0后,当前线程还需要调用AQS中的doReleaseShared方法来激活由于调用await方法而被阻塞的线程。

虽然每天要陪女票。但是每天的学习不能停止,依然是个会敲代码的汤姆猫!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值