🎈专栏链接:多线程相关知识详解
目录
一.CountDownLatch的介绍
二.CountDownLatch类里面的方法
三.CountDownLatch的两种应用场景
①一等多情况
②多等一情况
一.CountDownLatch的介绍
CountDownLatch是一种用来控制多线程的工具类,它被称为门阀、计数器或者闭锁.
二.CountDownLatch类里面的方法
CountDownLatch(int count){...} 类中唯一的构造器,用来构造一个用给定计数初始化的CountDownLatch
await(){...} 调用该方法的线程会阻塞,直到计数器count的值为0的时候才唤醒
countDown(){...} 如果当前计数大于零,则对其递减,当计数为0的时候等待的线程将会被唤醒
getCount(){...} 获得计数器的值,即当前还需要执行完几个线程
三.CountDownLatch的两种应用场景
①一等多情况
例如:老板需要检查工人需要干的活完成的情况如何,但老板得等待所有的工人全部完成工作的时候才去检查,即一等多
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class Worker implements Runnable{
private CountDownLatch downLatch;
private String name;
public Worker(CountDownLatch downLatch,String name){
this.downLatch = downLatch;
this.name = name;
}
@Override
public void run() {
this.doWork();
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));//随机休息1~10秒的时间
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(this.name + "活干完了!");
this.downLatch.countDown();//当前线程把计数器减一
System.out.println("剩余线程的个数: " + downLatch.getCount());
}
private void doWork(){
System.out.println(this.name + "正在干活!");
}
}
class Boss implements Runnable{
private CountDownLatch downLatch;
public Boss(CountDownLatch downLatch){
this.downLatch = downLatch;
}
@Override
public void run() {
System.out.println("老板正在等待所有工人干完活...");
try {
this.downLatch.await();//使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断,如果计数器为0则此方法立刻返回
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("工人的活都干完了,老板开始检查了!");
}
}
public class Demo1 {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();//使用线程池
CountDownLatch latch = new CountDownLatch(3);//设置线程运行次数
Worker worker1 = new Worker(latch,"张三");
Worker worker2 = new Worker(latch,"李四");
Worker worker3 = new Worker(latch,"王五");
Boss boss = new Boss(latch);
executor.execute(worker1);
executor.execute(worker2);
executor.execute(worker3);
executor.execute(boss);
executor.shutdown();//不再接受新的任务
}
}
运行结果:
由于线程的抢占式执行,前面的运行结果顺序可能是不一样的,但是老板开始检查一定是在所有的工人完成任务后才会开始执行的
②多等一情况
例如:运动会上的跑步比赛,所有的运动员得先在各自的跑道上等待裁判员的口令,当裁判员的宣布比赛开始的时候跑道上的运动员才能开始比赛.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class Runner implements Runnable{
private CountDownLatch downLatch;
private String name;
public Runner(CountDownLatch downLatch,String name){
this.downLatch = downLatch;
this.name = name;
}
@Override
public void run() {
System.out.println(name + "正在等待跑步口令");
try {
this.downLatch.await();//等待计数器的值减到0才会往下运行
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(name + "开始跑步!");
}
}
class Judge implements Runnable{
private CountDownLatch downLatch;
public Judge(CountDownLatch downLatch){
this.downLatch = downLatch;
}
@Override
public void run() {
System.out.println("裁判员发出口令!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
this.downLatch.countDown();//计数器减1
}
}
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();//使用线程池
CountDownLatch downLatch = new CountDownLatch(1);//因为只有一位且要等裁判员所以初始值为1
Runner runner1 = new Runner(downLatch,"1号");
Runner runner2 = new Runner(downLatch,"2号");
Runner runner3 = new Runner(downLatch,"3号");
Judge judge = new Judge(downLatch);
executor.execute(runner1);
executor.execute(runner2);
executor.execute(runner3);
Thread.sleep(3000);//先让上面能执行的先执行完,避免与下面的线程发生抢占式执行
executor.execute(judge);
executor.shutdown();//不再接受新的任务
}
}
运行结果:
为了避免抢占式执行,先让上面的线程能执行的先执行,然后再进行等待下面的线程执行,直到计数器的值变为0,才会开始执行剩下的内容