CountDownLatch倒计数锁存器
CountDownLatch:用于协同控制一个或多个线程等待在其他线程中执行的一组操作完成,然后再继续执行
CountDownLatch用法
- 构造方法:CountDownLatch(int count),count指定等待的条件数(任务数、操作数),不可再更改
- 等待方法:await(),阻塞等待线程直到count减少为0,count为0时,不会阻塞,继续执行
- boolean await(long timeout,TimeUnit
unit):可以设置超时时间的await方法,返回true表示等待条件到达;false表示条件未来到达,但超时了 - long getCount():获取当前计数值,常用于调试或者测试
ps:CountDownLatch注意事项:只可使用一次,不能重复使用,计数变为0之后,就不可再用
CountDownLatch适用场景
- 等待多个条件完成,countDownLatch(N)这个多个条件可以是:等待N个线程、等待N个操作、等待某操作的N次执行
- 用于并发测试,等待多个线程一起出发
CountDownLatch例子
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;public class JDKCountDown {private static final Random random = new Random(System.currentTimeMillis());public static void main(String[] args) throws InterruptedException {Optional.of("准备多线程处理任务。").ifPresent(System.out::println);final CountDownLatch countDownLatch = new CountDownLatch(5);IntStream.rangeClosed(1,5).forEach(i -> new Thread(()->{try {Optional.of("线程" + Thread.currentThread().getName() + " 开始执行任务。").ifPresent(System.out::println);Thread.sleep(random.nextInt(1000));Optional.of("线程" + Thread.currentThread().getName() + " 执行任务结束。").ifPresent(System.out::println);} catch (InterruptedException e) {e.printStackTrace();}countDownLatch.countDown();}, "Thread-"+i).start());countDownLatch.await();Optional.of("多线程处理任务结束。准备第二阶段的工作").ifPresent(System.out::println);Optional.of("------------------------------------").ifPresent(System.out::println);Optional.of("FINISH").ifPresent(System.out::println);}
}
执行之后的结果
准备多线程处理任务。
线程Thread-3 开始执行任务。
线程Thread-4 开始执行任务。
线程Thread-5 开始执行任务。
线程Thread-2 开始执行任务。
线程Thread-1 开始执行任务。
线程Thread-2 执行任务结束。
线程Thread-1 执行任务结束。
线程Thread-3 执行任务结束。
线程Thread-5 执行任务结束。
线程Thread-4 执行任务结束。
多线程处理任务结束。准备第二阶段的工作
------------------------------------
FINISH
我们手写一个简单SimpleCountDown
1.需要一个totalCount指定等待的条件数(任务数、操作数)
2.需要一个countDown计算有几个线程 结束了
上代码
/*** 简单倒计时工具类,用于多线程间协调完成计数操作*/
public class SimpleCountDown {/** 总需要达到的倒计数次数(不可变) */private final int totalCount;/** 当前倒计数值 */private int countDown;/** 是否被取消(volatile保证可见性) */private volatile boolean canceled = false;/*** 构造方法* @param totalCount 需要完成的总倒计数次数*/public SimpleCountDown(int totalCount) {this.totalCount = totalCount;}/*** 执行一次倒计数操作(线程安全)* <p>增加当前计数值并通知所有等待线程</p>*/public void down(){synchronized (this){this.countDown++;this.notifyAll(); // 通知所有等待线程检查条件}}/*** 等待直到完成所有倒计数操作* @throws InterruptedException 若当前线程被中断则抛出异常*/public void await() throws InterruptedException {synchronized (this){while (countDown != totalCount){ // 使用while防止虚假唤醒this.wait();}}}
}
测试类
import java.util.Optional;
import java.util.Random;
import java.util.stream.IntStream;/*** 演示SimpleCountDown倒计时门闩的使用示例类*/
public class SimpleCountDownClient {// 随机数生成器,用于模拟线程任务的随机执行时间private static final Random random = new Random(System.currentTimeMillis());public static void main(String[] args) throws InterruptedException {// 输出任务开始提示Optional.of("准备多线程处理任务。").ifPresent(System.out::println);// 初始化计数器为5,表示需要等待5个线程完成final SimpleCountDown countDownLatch = new SimpleCountDown(5);// 启动5个线程执行任务IntStream.rangeClosed(1, 5).forEach(i -> {new Thread(() -> {try {// 线程任务开始通知Optional.of("线程" + Thread.currentThread().getName() + " 开始执行任务。").ifPresent(System.out::println);// 模拟随机执行时间(0-1000毫秒)Thread.sleep(random.nextInt(1000));// 任务完成通知Optional.of("线程" + Thread.currentThread().getName() + " 执行任务结束。").ifPresent(System.out::println);} catch (InterruptedException e) {e.printStackTrace();} finally {// 通知计数器完成一个任务countDownLatch.down();}}, "Thread-" + i).start();});// 阻塞等待所有线程完成countDownLatch.await();// 所有线程完成后输出后续阶段提示Optional.of("多线程处理任务结束。准备第二阶段的工作").ifPresent(System.out::println);Optional.of("------------------------------------").ifPresent(System.out::println);Optional.of("FINISH").ifPresent(System.out::println);}
}
执行的结果
准备多线程处理任务。
线程Thread-1 开始执行任务。
线程Thread-5 开始执行任务。
线程Thread-3 开始执行任务。
线程Thread-4 开始执行任务。
线程Thread-2 开始执行任务。
线程Thread-5 执行任务结束。
线程Thread-1 执行任务结束。
线程Thread-2 执行任务结束。
线程Thread-3 执行任务结束。
线程Thread-4 执行任务结束。
多线程处理任务结束。准备第二阶段的工作
------------------------------------
FINISH