package com.xbz.thread.juc.countDownLatch;
import java.util.concurrent.CountDownLatch;
/**
* @title CountDownLatch类的应用
* CountDownLatch类位于java.util.concurrent(简称juc)包下 , 它是一个同步工具类 , 利用它可以实现类似计数器的功能
* 比如有一个任务A , 它要等待其他4个任务结束执行之后才能执行 , 此时就可以利用CountDownLatch来实现这种功能了
* 主要用来协调多个线程之间的同步 , 或者说起到线程之间的通信 ( 而不是用作互斥的作用 )
* CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后 , 再继续执行 . 使用一个计数器进行实现 .
* 计数器初始值为线程的数量 .
* 当每一个线程完成自己任务后 , 计数器的值就会减一(手动) .
* 当计数器的值为0时 , 表示所有的线程都已经完成了任务 , 然后在CountDownLatch上等待的线程就可以恢复执行任务
*
* CountDownLatch类只提供了一个构造器 :
* public CountDownLatch(int count)//参数count为计数值 , 即需要先执行几个线程
*
* CountDownLatch类的主要方法 :
* void await()//调用await()方法的线程会被挂起 , 它会等待直到count值为0才继续执行
* boolean await(long timeout, TimeUnit unit)//和await()类似 , 只不过等待一定的时间后count值还没变为0的话也会继续执行
* void countDown()//将count值减1
* CountDownLatch的不足
* CountDownLatch是一次性的 , 计数器的值只能在构造方法中初始化一次 , 之后没有任何机制再次对其设置值 , 当CountDownLatch使用完毕后 , 它不能再次被使用
* @author Xingbz
* @createDate 2018-7-18
*/
public class MainDemo {
public static void main(String[] args) throws Exception {
// demo1();
demo2();
}
/**
* @title CountDownLatch典型用法1
* @description 某一线程在开始运行前等待n个线程结束执行 .
* 将CountDownLatch的计数器初始化为n new CountDownLatch(n) ,
* 每当一个任务线程结束执行 , 就将计数器减1 countdownlatch.countDown() ,
* 当计数器的值变为0时 , 在CountDownLatch上 await() 的线程就会被唤醒 .
* 一个典型应用场景就是启动一个服务时 , 主线程需要等待多个组件加载完毕 , 之后再继续执行 .
* @author Xingbz
*/
public static void demo1() throws Exception {
// final CountDownLatch latch = new CountDownLatch(2);//2个线程后开始执行
final CountDownLatch latch = new CountDownLatch(2) {//此处是为了演示线程过程特意重写了await方法, 实际应用如上声明即可
@Override
public void await() throws InterruptedException {
System.out.println("当前线程 : " + Thread.currentThread().getName() + " 进入阻塞状态 . 当前计数器 : " + this.getCount());
super.await();
}
};//2个线程后开始执行
runThread("线程1", 2000L, latch);
runThread("线程2", 3000L, latch);
System.out.println("等待2个子线程结束执行...");
latch.await();//当前线程挂起 , 直到计数器为0
// latch.await(20, TimeUnit.SECONDS);//最多阻塞20S , 20S后即使计数器不是0也继续往下执行
System.out.println("2个子线程已经结束执行");
System.out.println("继续执行主线程");
}
/**
* @title CountDownLatch典型用法2
* @description
* 实现多个线程开始执行任务的最大并行性 .
* 注意是并行性 , 不是并发 , 强调的是多个线程在某一时刻同时开始执行 .
* 类似于赛跑 , 将多个线程放到起点 , 等待发令枪响 , 然后同时开跑 .
* 做法是额外初始化一个共享的CountDownLatch(1) , 将其计数器初始化为1 ,
* 多个线程在开始执行任务前首先 coundownlatch.await() , 当主线程调用 countDown() 时 , 计数器变为0 , 多个线程同时被唤醒
* @author Xingbz
*/
public static void demo2() throws Exception {
CountDownLatch countDown = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(5);
for (int i = 0; i < 5; ++i) {// 依次创建并启动处于等待状态的5个线程
runThread("线程" + (i + 1), 2000L, countDown, latch);
}
System.out.println("执行了5个线程并进入了阻塞状态 . 等待统一启动 . . . ");
countDown.countDown();
System.out.println("主控器结束计数 , 所有线程同时开始执行 . . .");
latch.await();
System.out.println("完成");
}
/**
* @title 模拟启动一个子线程工作
* @param threadName 线程名称
* @param sleepTime 休眠时间
* @param countDown 主控制器 CountDownLatch 负责所有线程启动后先进入阻塞 "预备" 状态
* @param latch 辅控制器 CountDownLatch 负责所有线程执行完成后继续运行主线程
* @author Xingbz
*/
private static void runThread(String threadName, Long sleepTime, CountDownLatch countDown, CountDownLatch latch) {
Thread thread = new Thread(() -> {
try {
System.out.println("子线程[" + Thread.currentThread().getName() + "]准备执行");
countDown.await();
Thread.sleep(sleepTime);
System.out.println("子线程[" + Thread.currentThread().getName() + "]正在执行");
Thread.sleep(sleepTime);
latch.countDown();//子线程结束执行 , latch计数器减1
System.out.println("子线程[" + Thread.currentThread().getName() + "]结束执行 , latch计数器-1 . 当前计数器 : " + latch.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, threadName);
thread.start();
}
/**
* @title 模拟启动一个子线程工作
* @param threadName 线程名称
* @param sleepTime 休眠时间
* @param latch CountDownLatch
* @author Xingbz
*/
private static void runThread(String threadName, Long sleepTime, CountDownLatch latch) {
Thread thread = new Thread(() -> {
try {
System.out.println("子线程[" + Thread.currentThread().getName() + "]正在执行");
Thread.sleep(sleepTime);
latch.countDown();//子线程结束执行 , latch计数器减1
System.out.println("子线程[" + Thread.currentThread().getName() + "]结束执行 , latch计数器-1 . 当前计数器 : " + latch.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, threadName);
thread.start();
}
}