java并发计数器,Java高并发编程基础三大利器之CountDownLatch

# 引言

上一篇文章咱们介绍了AQS的信号量Semaphore《Java高并发编程基础三大利器之Semaphore》,接下来应该轮到CountDownLatch了。html

什么是CountDownLatch

CountDownLatch是经过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就减1,当计数器的值为0时,表示全部线程都执行完毕,而后在闭锁上(调用await方法的线程)等待的线程就能够恢复工做了。前端

应用场景

CountDownLatch能够用来干什么呢?有什么应用场景?实际项目中有应用的场景吗?这应该才是你们比较关心的。咱们先来看看官网提供的例子是如何进行应用的https://docs.oracle.com/javas...

官方提供了两个demo我直接把它转成了图片顺带推荐下这个代码转图片的网址https://www.dute.org/code-sna... 还挺好用的。java

官网demo1

The first is a start signal that prevents any worker from proceeding until the driver is ready for them to proceed;

The second is a completion signal that allows the driver to wait until all workers have completed.

第一个开始信号(startSignal)会阻止任何工人(worker )开始工做,在司机到来以前。说白了就是司机没来工人就不能干活。

第二个是完成信号 (doneSignal),容许司机 Driver 等待,直到全部的工人完成.说白了就是司机要等到全部工人完工为止。

1460000039390121

官网demo2

Another typical usage would be to divide a problem into N parts, describe each part with a Runnable that executes that portion and counts down on the latch, and queue all the Runnables to an Executor. When all sub-parts are complete, the coordinating thread will be able to pass through await.

另外一种典型的用法就是把一个大任务拆分N个部分,让多个线程(Worker)执行,每一个线程(Worker)执行完本身的部分计数器就减1,当全部子部分都完成后,Driver 才继续向下执行才继续执行。

就比如富士康手机加工的流水线同样,组装一步手机须要一条条的流水线来相互配合完成。一条条流水线(Worker),每条线都干本身的活。有的流水线是贴膜的,有的流水线是打螺丝的,有的流水线是质检的、有的流水线充电的、有的流水线贴膜的。等这些流水线都干完了就把一部手机组装完成了。编程

1460000039390122

上面两个就是官方提供的demo,下面我再来两个咱们平时开发中能够用到的栗子:api

多个线程等待:模拟并发,让并发线程一块儿执行。

有时候咱们写了接口想去压测下它,看看它的最大并发数大概是多少。固然咱们可使用Jmeter来进行压测,可是有时候咱们不想去下载工具,其实就能够借助CountDownLatch来实现。并发

/**

* @author: 公众号:java金融

*/

public class TestCountDownLatch1 {

public static void main(String[] args) throws InterruptedException {

CountDownLatch countDownLatch = new CountDownLatch(1);

for (int i = 0; i < 5; i++) {

new Thread(() -> {

try {

//全部请求都阻塞在这,等待

countDownLatch.await();

// 调用测试接口

System.out.println(Thread.currentThread().getName() + "开始执行……");

} catch (InterruptedException e) {

e.printStackTrace();

}

}).start();

}

// 让请求都准备好

Thread.sleep(2000);

// 让全部请求统一请求

countDownLatch.countDown();

}

}

咱们经过CountDownLatch.await(),让多个参与者线程启动后阻塞等待,而后在主线程 调用CountDownLatch.countdown() 将计数减为0,让全部线程一块儿往下执行;以此实现了多个线程在同一时刻并发执行,来模拟并发请求的目的。oracle

单个线程等待:多个线程(任务)完成后,进行汇总合并

/**

* @author: 公众号:java金融

*/

public class TestCountDownLatch1 {

public static void main(String[] args) throws InterruptedException {

int count = 3;

CountDownLatch countDownLatch = new CountDownLatch(count);

for (int i = 0; i < count; i++) {

final int index = i;

new Thread(() -> {

try {

Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(1000));

System.out.println("finish" + index + Thread.currentThread().getName());

} catch (InterruptedException e) {

e.printStackTrace();

}finally{

countDownLatch.countDown();

}

}).start();

}

countDownLatch.await();// 主线程在阻塞,当计数器==0,就唤醒主线程往下执行。

System.out.println("主线程:在全部任务运行完成后,进行结果汇总");

}

}

这种场景应该是用的最多了,好比咱们打开一个电商的我的中心页面,咱们须要调用,用户信息接口、用户订单接口、用户会员信息等接口,而后合并后一块儿给到前端,假设每一个接口最长耗时为1s,若是咱们同步调用的话最大耗时时间是3s,若是咱们采用异步调用而后合并结果,因此最大的耗时时间是3s。每一个接口调用返回数据后调用countDown方法,让计数器进行减1,当把计数器减为0时的这个线程会去唤醒主线程,让它继续往下走。dom

CountDownLatch 实现原理

CountDownLatch是经过AQS的state字段来实现的一个计数器,计数器的初始值(state的值)为new CountDownLatch设置的数量,每次调用countDown的时候,state的值会进行减1,最后某个线程将state值减为0时,会把调用了await()进行阻塞等待的线程进行唤醒。CountDownLatch重写了tryReleaseShared这个方法,只有当state这个字段被设置为0时,也就是tryReleaseShared返回true的状况就会执行doReleaseShared方法,把调用了await的线程进行唤醒。异步

public final boolean releaseShared(int arg) {

if (tryReleaseShared(arg)) {

doReleaseShared();

return true;

}

return false;

}

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的其余源码就不进行分析了,

相信看了这两篇文章《Java高并发编程基础之AQS》、《Java高并发编程基础三大利器之Semaphore》再来看这个仍是比较轻松的。ide

总结

CountDownLatch不能从新初始化或者修改CountDownLatch内部计数器的值。

CountDownLatch和Semaphore在使用AQS的方式上很类似,在同步状态中都是保存的是当前的计数值。

CountDownLatch的做用就是容许一个或多个线程等待其余线程完成操做,看起来有点相似join() 方法,但其提供了比 join() 更加灵活的API。

CountDownLatch能够手动控制在n个线程里调用n次countDown()方法使计数器进行减一操做,也能够在一个线程里调用n次执行减一操做。

join() 的实现原理是不停检查join线程是否存活,若是 join 线程存活则让当前线程永远等待。因此二者之间相对来讲仍是CountDownLatch使用起来较为灵活。

结束

因为本身才疏学浅,不免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。

若是你以为文章还不错,你的转发、分享、赞扬、点赞、留言就是对我最大的鼓励。

感谢您的阅读,十分欢迎并感谢您的关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值