CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。
当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
案例一:
案例二:
CountDownLatch使用场景
线程计数器 用于线程执行任务,计数 等待线程结束
用法一: 等待所有的事情都做完
//程序计数器
CountDownLatch countDownLatch = new CountDownLatch(10000);//2个线程
ExecutorService executorService = Executors.newFixedThreadPool(2);AtomicInteger count = new AtomicInteger(0);
for (int i = 0; i < 10000; i++) {executorService.submit(() -> {
count.getAndIncrement();//自增
System.out.println(Thread.currentThread().getName() + " : " + count.get());
countDownLatch.countDown();
});
}
//线程池 等待10s
executorService.awaitTermination(10, TimeUnit.SECONDS);//关闭线程 其实是将线程状态设置为中断标志 必须等待所有线程处理完任务,才能完全关闭
executorService.shutdown();//必须等待两个线程执行完 会一直等待下去,当然也可以设置指定时间等待超时 await(timeout);
countDownLatch.await();
}
始终是2个线程在做事情,等2个线程做完事情才会停止下来。
用法二:假设2个线程做事情,刚开始并行做事情,等一个执行完成之后,另一个才能执行(实际还是计数
)
//程序计数器
CountDownLatch countDownLatch = new CountDownLatch(1);Thread thread1 =new Thread(()->{
System.out.println(" ---------------- 1 准备 ---------------- ");
try {
countDownLatch.await();
System.out.println(" ---------------- 1 finsh ---------------- ");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();Thread thread2 = new Thread(() -> {
System.out.println(" ---------------- 2 准备 ---------------- ");
try {
Thread.sleep(1_000);
System.out.println(" ---------------- 异步做事情 ---------------- ");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();
}
});
thread2.start();
//main 在等main 结束 死循环
Thread.currentThread().join();
刚开始一起在准备状态,然后分开做事情
用法三:退出条件
中断一个线程 count 到0
//程序计数器
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = Thread.currentThread();
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 1 中断条件1
countDownLatch.countDown();});
thread1.start();
countDownLatch.await();
System.out.println(" ----------------- ");
}
等待时间中断
//程序计数器
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = Thread.currentThread();
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});thread1.start();
//2 中断条件3
countDownLatch.await(5, TimeUnit.SECONDS);System.out.println(" ----------------- ");
}
就中断条件而言: 当前还可以父线程中断
//程序计数器
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断条件3
thread.interrupt();
});thread1.start();
System.out.println(" ----------------- ");
用法四: 封装结束完后通知事件
封装结束完后通知事件 参考 CyclicBarrier 通知
public class CountDownLatchTest4 extends CountDownLatch {
private Runnable runnable;
public CountDownLatchTest4(int count, Runnable runnable) {
super(count);
this.runnable = runnable;
}@Override
public void countDown() {
super.countDown();
if (super.getCount()==0){
runnable.run();
}
}public static void main(String[] args) throws InterruptedException {
//程序计数器
CountDownLatchTest4 countDownLatch = new CountDownLatchTest4(1,()->{System.out.println(" 计数结束 .... ");
});Thread thread1 = new Thread(() -> {
try {
Thread.sleep(2_000);
countDownLatch.countDown();
System.out.println(" thread 1 do something ");
} catch (InterruptedException e) {
e.printStackTrace();
}
});Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1_000);
System.out.println(" thread 2 do something ");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});thread1.start();
thread2.start();countDownLatch.await();
System.out.println(" ----------------- main 结束 ----------------- ");
}
}
可以看到运行结束,通知事件
自定义计数器
当然我们也可以实现自己的计数器
/**
* 自定义 CountDown 计数器
*/
public class CountDown {
//计数器
private int count = 0;
private final int total;public CountDown(int total) {
this.total = total;
}
public void countDown() {
synchronized (this) {
this.count++;
//锁住 ++ 通知其他线程
this.notifyAll();
}
}
public void aWait() throws InterruptedException {
synchronized (this) {
while (total != count) {
//不等于 则 继续等待
this.wait();
}
}
}
}
测试
CountDown countDown = new CountDown( 5);System.out.println(" 准备多线程处理任务 ");
IntStream.rangeClosed(1, 5).forEach(x -> {
new Thread(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" 线程开始 ----- " + Thread.currentThread().getName());
countDown.countDown();
}, x + "").start();});
try {
countDown.aWait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" 准备多线程处理任务 结束 ");
System.out.println(" ---------------------- ");
System.out.println(" 结束 mian ---------- ");
}
测试结果
最后
CountDownLatch 可以用来计数,可以测试任务是否执行结束
也可以用来停止一个线程,也可以用来线程运行结束完后通知事件,彼此工作的线程互相独立不关心。