CountDownLatch是具备synchronized机制的一个工具,目的是让一个或者多个线程等待,直到其余线程的一系列操做完成。java
CountDownLatch初始化的时候,须要提供一个整形数字,数字表明着线程须要调用countDown()方法的次数,当计数为0时,线程才会继续执行await()方法后的其余内容。
CountDownLatch(int count);数据库
getCount: 返回当前的计数count值,
public void countDown() 调用此方法后,会减小计数count的值。 递减后若是为0,则会释放全部等待的线程
public void await() throws InterruptedException 调用CountDownLatch对象的await方法后。 会让当前线程阻塞,直到计数count递减至0。
若是当前线程数大于0,则当前线程在线程调度中将变得不可用,并处于休眠状态,直到发生如下两种状况之一:segmentfault
一、调用countDown()方法,将计数count递减至0。安全
二、当前线程被其余线程打断。框架
public boolean await(long timeout, TimeUnit unit) throws InterruptedException
同时await还提供一个带参数和返回值的方法。ide
若是计数count正常递减,返回0后,await方法会返回true并继续执行后续逻辑。函数
或是,还没有递减到0,而到达了指定的时间间隔后,方法返回false。工具
若是时间小于等于0,则此方法不执行等待。学习
首先创建3个线程。ui
public class Worker1 implements Runnable { @Override public void run() { System.out.println("-线程1启动"); try { Thread.sleep(13_000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程1完成--我休眠13秒\r\n"); } } public class Worker2 implements Runnable { @Override public void run() { System.out.println("-线程2启动"); try { Thread.sleep(3_000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程2完成--我休眠3秒\r\n"); } } public class Worker3 implements Runnable { @Override public void run() { System.out.println("-线程3启动"); try { Thread.sleep(3_000); } catch (InterruptedException e) { e.printStackTrace(); } try { Thread.sleep(3_000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程3完成--我休眠6秒\r\n"); System.out.println(); } } public class Main { public static void main(String[] args) throws InterruptedException { Worker1 worker1 = new Worker1(); Worker2 worker2 = new Worker2(); Worker3 worker3 = new Worker3(); Thread thread1 = new Thread(worker1,"线程1"); Thread thread2 = new Thread(worker2,"线程2"); Thread thread3 = new Thread(worker3,"线程3"); thread1.start(); thread2.start(); thread3.start(); thread1.join(); thread2.join(); thread3.join(); System.out.println("主线程结束...."); } }
打印结果以下:
-线程3启动 -线程2启动 -线程1启动 线程2完成--我休眠3秒 线程3完成--我休眠6秒 线程1完成--我休眠13秒 主线程结束.... Process finished with exit code 0
能够看出三个线程是并行执行的。启动顺序,并不和执行完毕的顺序一致,但能够明确的是,主线程为一直阻塞,直到三个线程执行完毕。
阿里巴巴的数据库链接池Druid中也用了countDownLatch来保证初始化。
// 开启建立链接的线程,若是线程池createScheduler为null, //则开启单个建立链接的线程 createAndStartCreatorThread(); // 开启销毁过时链接的线程 createAndStartDestroyThread();
本身编写一个例子:
这里模拟一种状况:
主线程 依赖 线程A初始化三个数据,才能继续加载后续逻辑。
public class CountDownArticle { /** * 模拟 主线程 依赖 线程A初始化一个数据,才能继续加载后续逻辑 */ public static void main(String[] args) throws InterruptedException { AtomicReference<String> key = new AtomicReference<>(""); CountDownLatch countDownLatch = new CountDownLatch(3); Thread t = new Thread(() -> { try { //休眠5秒,模拟数据的初始化 TimeUnit.SECONDS.sleep(5); key.set("核心秘钥123456"); System.out.println("数据1初始化完毕"); //释放---此处能够在任何位置调用,很灵活 countDownLatch.countDown(); System.out.println("数据2初始化完毕"); countDownLatch.countDown(); System.out.println("数据3初始化完毕"); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }); t.start(); //等待数据初始化,阻塞 countDownLatch.await(); System.out.println("key:" + key.get()); } }
打印内容以下:
数据1初始化完毕 数据2初始化完毕 数据3初始化完毕 key:核心秘钥123456
在使用join()中,多个线程只有在执行完毕以后欧才能被解除阻塞,而在CountDownLatch中,线程能够在任什么时候候任何位置调用countdown方法减小计数,经过这种方式,咱们能够更好地控制线程的解除阻塞,而不是仅仅依赖于链接线程的完成。
join()方法的执行逻辑以下图所示:
从源码能够看出,CountDownLatch是依赖于AbstractQueuedSynchronizer来实现这一系列逻辑的。
队列同步器AbstractQueuedSynchronizer
是一个用来构建锁和同步器的框架,它在内部定义了一个被标识为volatile的名为state的变量,用来表示同步状态。
多个线程之间能够经过AQS来独占式或共享式的抢占资源。
而且它经过内置的FIFO队列来完成线程的排队工做。
CountDownLatch中的Sync会优先尝试修改state的值,来获取同步状态。例如,若是某个线程成功的将state的值从0修改成1,表示成功的获取了同步状态。 这个修改的过程是经过CAS完成的,因此能够保证线程安全。
反之,若是修改state失败,则会将当前线程加入到AQS的队列中,并阻塞线程。
CountDownLatch(int N) 中的计数器,可让咱们支持最多等待N个线程的操做完成,或是一个线程操做N次。
若是仅仅只须要等待线程的执行完毕,那么join可能就能知足。可是若是须要灵活的控制线程,使用CountDownLatch。
countDownLatch.countDown();
这一句话尽可能写在finally中,或是保证此行代码前的逻辑正常运行,由于在一些状况下,出现异常会致使没法减一,而后出现死锁。
CountDownLatch 是一次性使用的,当计数值在构造函数中初始化后,就不能再对其设置任何值,当 CountDownLatch 使用完毕,也不能再次被使用。
为了方便你们学习讨论,我建立了一个java疑难攻坚互助你们庭,和其余传统的学习交流不一样。本群主要致力于解决项目中的疑难问题,在遇到项目难以解决的
问题时,均可以在这个你们庭里寻求帮助。
公众号回复【问题的答案】进入:java中Integer包装类的基本数据类型是?
若是你也经历过遇到项目难题,无从下手,
他人有可能能够给你提供一些思路和见解,一百我的就有一百种思路,
一样,若是你也乐于帮助别人,那解决别人遇到的问题,也一样对你是一种锻炼。
欢迎来公众号【侠梦的开发笔记】 ,回复干货,领取精选学习视频一份