疯狂创客圈 经典图书 : 《Netty Zookeeper Redis 高并发实战》 面试必备 + 面试必备 + 面试必备 【博客园总入口 】html
疯狂创客圈 经典图书 : 《SpringCloud、Nginx高并发核心编程》 大厂必备 + 大厂必备 + 大厂必备 【博客园总入口 】前端
入大厂+涨工资必备: 高并发【 亿级流量IM实战】 实战系列 【 SpringCloud Nginx秒杀】 实战系列 【博客园总入口 】java
Java的concurrent包里面的CountDownLatch其实能够把它看做一个计数器,只不过这个计数器的操做是原子操做,同时只能有一个线程去操做这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。面试
你能够向CountDownLatch对象设置一个初始的数字做为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器的计数值被其余的线程减为0为止。编程
CountDownLatch的一个很是典型的应用场景是:有一个任务想要往下执行,但必需要等到其余的任务执行完毕后才能够继续往下执行。假如咱们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其余的任务执行完本身的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。服务器
好比:客户端一次请求5个统计数据,服务器须要所有统计完成后,才返回客户端,可使用CountDownLatch 。多线程
//参数count为计数值 public CountDownLatch(int count) { };
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 public void await() throws InterruptedException { }; //和await()相似,只不过等待必定的时间后count值还没变为0的话就会继续执行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //将count值减1 public void countDown() { };
CountDownLatch countDown = new CountDownLatch(2)
countDown.countDown()
countDown.await()
实现阻塞同步package cn.day13; import java.util.concurrent.CountDownLatch; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub final CountDownLatch latch = new CountDownLatch(2); new Thread() { public void run() { try { System.out.println("子线程" + Thread.currentThread().getName() + "正在执行"); Thread.sleep(3000); System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread() { public void run() { try { System.out.println("子线程" + Thread.currentThread().getName() + "正在执行"); Thread.sleep(3000); System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); try { System.out.println("等待2个子线程执行完毕..."); latch.await(); System.out.println("2个子线程已经执行完毕"); System.out.println("继续执行主线程"); } catch (InterruptedException e) { e.printStackTrace(); } } }
打印结果:并发
子线程Thread-0正在执行 等待2个子线程执行完毕... 子线程Thread-1正在执行 子线程Thread-0执行完毕 子线程Thread-1执行完毕 2个子线程已经执行完毕 继续执行主线程
前面给了一个demo演示如何用,那这个东西在实际的业务场景中是否会用到呢?高并发
由于确实在一个业务场景中使用到了,否则也就不会单独捞出这一节...工具
电商的详情页,由众多的数据拼装组成,如能够分红一下几个模块
上面的几个模块信息,都是从不一样的服务获取信息,且彼此没啥关联;因此为了提升响应,彻底能够作成并发获取数据,如
可是最终拼装数据并返回给前端,须要等到上面的全部信息都获取完毕以后,才能返回,这个场景就很是的适合 CountDownLatch
来作了
CountDownLatch#await(long, TimeUnit)
等待全部的模块信息返回CountDownLatch#countDown()
进行计数-1 CountDownLatch在多线程并发编程中充当一个计时器的功能,而且内部维护一个count的变量,而且其操做都是原子操做,该类主要经过countDown()和await()两个方法实现功能的,首先经过创建CountDownLatch对象,而且传入参数即为count初始值。
若是一个线程调用了await()方法,那么这个线程便进入阻塞状态,并进入阻塞队列。若是一个线程调用了countDown()方法,则会使count-1;当count的值为0时,这时候阻塞队列中调用await()方法的线程便会逐个被唤醒,从而进入后续的操做。好比下面的例子就是有两个操做,一个是读操做一个是写操做,如今规定必须进行完写操做才能进行读操做。因此当最开始调用读操做时,须要用await()方法使其阻塞,当写操做结束时,则须要使count等于0。所以count的初始值能够定为写操做的记录数,这样即可以使得进行完写操做,而后进行读操做。
内部也是有个Sync
类继承了AQS
,因此CountDownLatch
类的构造方法就是调用Sync
类的构造方法,而后调用setState()
方法设置AQS
中state
的值。
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } Sync(int count) { setState(count); }
该方法是使调用的线程阻塞住,直到state
的值为0就放开全部阻塞的线程。实现会调用到AQS
中的acquireSharedInterruptibly()
方法,先判断下是否被中断,接着调用了tryAcquireShared()
方法,讲AQS
那篇文章里提到过这个方法是须要子类实现的,能够看到实现的逻辑就是判断state
值是否为0,是就返回1,不是则返回-1。
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; }
这个方法会对state
值减1,会调用到AQS
中releaseShared()
方法,目的是为了调用doReleaseShared()
方法,这个是AQS定义好的释放资源的方法,而tryReleaseShared()
则是子类实现的,能够看到是一个自旋CAS
操做,每次都获取state
值,若是为0则直接返回,不然就执行减1的操做,失败了就重试,若是减完后值为0就表示要释放全部阻塞住的线程了,也就会执行到AQS
中的doReleaseShared()
方法。
public void countDown() { sync.releaseShared(1); } 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; } }
疯狂创客圈 - Java高并发研习社群,为你们开启大厂之门