eclipse工具类 源码_Java并发包工具类-CountDownLatch的源码剖析

本篇文章的主要内容如下:

1:举例说明CountDownLatch和Thread的join()方法2:CountDownLatch的原理剖析3:CountDownLatch的源码解析

1:举例说明CountDownLatch和Thread的join()方法

在实际的项目中,我们可能碰到如下的需求:

A线程需要用到B线程,C线程,D线程的结果

像上面的需求我们怎样利用程序去实现的。

实现一:熟悉Thread类的同学很快就能够想到join()方法,程序如下:

public class ThreadJoinTest { private static int totalCount; private static int count1; private static int count2; private static int count3; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> count1 = 10); Thread t2 = new Thread(() -> count2 = 20); Thread t3 = new Thread(() -> count3 = 30); t1.start(); t2.start(); t3.start(); t1.join(); t2.join(); t3.join(); totalCount = count1 + count2 + count3; System.out.println("totalCount=" + totalCount); }}

运行结果如下:

97f98fc318ab1edae112bef48202dbb9.png

上面的程序无论运行多少次都是同样的结果,我们看一下Thread的join()方法的原理:如果线程thread调用join()方法,那么它会阻塞当前正在运行的线程,记着是阻塞当前正在运行的线程,而不是阻塞thread线程,直到thread线程运行结束,才会继续往下执行

例如上面的例子中主线程执行到t1.join()方法时,主线程会被阻塞,直到线程t1运行结束,才能继续往下执行t2.join(),当执行t2.join()方法时,主线程序又被阻塞直到线程t2运行结束,以此类推,我们想象以下如果不调用join()方法,那结果是无法预知的。

实现二:利用CountDownLatch实现public class CountDownLatchTest { private static int totalCount; private static int count1; private static int count2; private static int count3; private static CountDownLatch cdl = new CountDownLatch(3); public static void main(String[] args) throws InterruptedException { new Thread(()->{ count1=10; cdl.countDown(); }).start(); new Thread(()->{ count2=20; cdl.countDown(); }).start(); new Thread(()->{ count3=30; cdl.countDown(); }).start(); cdl.await(); totalCount = count1+count2+count3; System.out.println("totalCount=" + totalCount); }}

运行结果如下:

b91858b09057d1ae11c5954fd0412e8d.png

从上面的运行结果可以看出,CountDownLatch也能实现join的效果,那它的实现原理是什么呢?和join()方法又有哪些区别呢?接下来从源码角度去揭开它的原理。

2:CountDownLatch的原理剖析

CountDownLatch是一个并发工具类,它允许一个线程或者多个线程等待,直到一系列操作在其他线程被完成,这句话蕴含了两层含义:

第一层含义:允许一个线程或者多个线程等待,是说明调用它的await()方法会使当前运行的线程阻塞。第二层含义:直到一系列操作在其他线程被完成,这个完成点就是调用了countDown()方法

CountDownLatch构造函数接收一个计数器,调用一次countDown()方法,计数器就会减1,只有计数器变为0时,等待的线程才能从await()方法返回继续执行下面的代码,例如如果初始化的计数器是2,那么必须调用2次countDown()方法,被阻塞的线程才能从await()中返回,继续执行下面的代码。

在我们启动一个服务时,可能有比较耗时的工作需要其他线程协助同时完成,只有这些工作完成后,主服务线程才能执行下去,我们可以利用Thread的join()方法完成,也可以通过CountDownLatch完成,但是两者又有所区别。

1:对于join(),必须线程逻辑完全执行,等待线程才能从阻塞中返回。2:对与CountDownLatch则非常的灵活了,它能保证调用countDown()方法前的逻辑能够在等待线程前执行,countDown()方法后的代码逻辑那就不一定了。

CountDownLatch的底层实现逻辑就是AQS共享资源模式的逻辑,其中初始化的计数器就是AQS共享资源state。

await()方法会阻塞当前线程,直到出现下面的情况才返回

1:计数器变成了02:被其他线程中断

countDown()方法的作用就是将计数器减一

对于这个并发工具类总结四个重要的知识点如下:

1:CountDownLatch底层是通过AQS框架的共享模式操作完成的。2:调用构造函数传递计数器count,并且计数器不能小于0,计数器会赋值给底层AQS的state。3:调用await()会阻塞当前线程,直到线程被中断或者count==0才返回4:每调用一次countDown()方法会将计时器count减一

3:CountDownLatch的源码解析

上面我们知道了它的原理,接下来我们从源码角度看一下它的实现。

在CountDownLatch源码中有这样一段代码:

private static final class Sync extends AbstractQueuedSynchronizer { //设置共享资源state Sync(int count) { setState(count); } int getCount() { return getState(); }//尝试获取共享资源 protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; }//尝试释放共享资源 protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; } }}

在CountDownLatch中创建了一个内部类Sync,它继承了AQS,这也是AQS的经典用法,并且实现了两个重要的方法,尝试获取共享资源tryAcquireShared()和释放共享资源tryReleaseShared().说明底层使用的是AQS框架的共享模式。对于AQS共享模式下资源的获取和释放不熟悉的请看这一篇文章。

public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count);}

上面是它的构造函数,传递一个参数计时器count,并且count必须大于等于0,如果小于0会抛出异常,然后它直接调用了Sync的构造函数,我们从Sync的构造函数可知,其实传递的count就是赋值给了AQS的共享资源变量state。

f518856e82703b91bd5f9564854ace7e.png
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1);}
59c5eb9b49f64ab656bdb98093fb0a42.png

上面的代码是CountDownLatch中的await()方法的源码,它底层直接调用了Sync的acquireSharedInterruptibly()方法,这个方法有两种情况返回:

第一种:被其他线程中断第二种:tryAcquireShared()<0

那tryAcquireShard()什么时候会小于0呢?这在Sync中可以找到答案。

9fd7e24bb703237a5ee64cf2219cbac7.png

上面我们知道了要想从await()方法返回,要么线程被中断,要么tryAcquireShared()<0,而此方法只有count==0时才小于0,上面我们在创建CountDownLatch对象时传递的共享资源是count,所以我们做的就是将count变成0,那就是我们调用countDown()方法了。

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

上面的countDown()方法直接调用了Sync的releaseShared()方法

8ace7af50df3a7f5d1123dfc4939f503.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值