学而不思则罔,思而不学则殆
【Java】CountDownLatch简单使用
CountDownLatch简单介绍
用来等待某些任务完成后继续后续的工作。
CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
CountDownLatch不足
CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,CountDownLatch使用完毕后,它不能再次被使用。
使用场景
使用多线程下载文件,每个线程下载一部分,当每个线程都下载完成时整个文件下载完成
简单测试
测试countDown
private static void test1() {
CountDownLatch countDownLatch = new CountDownLatch(3);
System.out.println(System.currentTimeMillis() + " " + countDownLatch.getCount());
countDownLatch.countDown();
System.out.println(System.currentTimeMillis() + " " + countDownLatch.getCount());
countDownLatch.countDown();
System.out.println(System.currentTimeMillis() + " " + countDownLatch.getCount());
countDownLatch.countDown();
System.out.println(System.currentTimeMillis() + " " + countDownLatch.getCount());
countDownLatch.countDown();
try {
System.out.println(System.currentTimeMillis() + " " + countDownLatch.getCount());
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + " end");
}
初始化了一个数量为3的计数器,调用四次countDown方法。
测试结果:
1603885038335 3
1603885038335 2
1603885038335 1
1603885038335 0
1603885038335 0
1603885038335 end
计数变为0后,调用countDown,还是0。调用await方法会立即成功(当前计数为0)。
测试多线程下载demo
//模拟自下载下载文件
static class DownloadTask implements Runnable {
int downloadId;
CountDownLatch countDownLatch;
public DownloadTask(int downloadId, CountDownLatch countDownLatch) {
this.downloadId = downloadId;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread() + " " + System.currentTimeMillis() + " 开始下载-" + downloadId);
// TODO: 2020/10/28 用休眠代替子线程执行耗时任务
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
System.out.println(Thread.currentThread() + " " + System.currentTimeMillis() + " 下载结束-" + downloadId + " getCount:" + countDownLatch.getCount());
}
}
}
private static void test2() {
final CountDownLatch countDownLatch = new CountDownLatch(3);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(new DownloadTask(1, countDownLatch));
executorService.submit(new DownloadTask(2, countDownLatch));
executorService.submit(new DownloadTask(3, countDownLatch));
try {
System.out.println(Thread.currentThread() + " " + System.currentTimeMillis() + " 等待整个文件下载完成");
countDownLatch.await();
System.out.println(Thread.currentThread() + " " + System.currentTimeMillis() + " 整个文件下载完成 getCount:" + countDownLatch.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
测试结果:
Thread[main,5,main] 1603890741458 等待整个文件下载完成
Thread[pool-1-thread-1,5,main] 1603890741458 开始下载-1
Thread[pool-1-thread-2,5,main] 1603890741463 开始下载-2
Thread[pool-1-thread-3,5,main] 1603890741463 开始下载-3
Thread[pool-1-thread-3,5,main] 1603890746463 下载结束-3 getCount:2
Thread[pool-1-thread-2,5,main] 1603890748463 下载结束-2 getCount:0
Thread[main,5,main] 1603890748463 整个文件下载完成 getCount:0
Thread[pool-1-thread-1,5,main] 1603890748463 下载结束-1 getCount:0
当countDown等于0的时候,才会往下执行
await 超时逻辑
private static void test3() {
CountDownLatch countDownLatch = new CountDownLatch(1);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(new Runnable() {
@Override
public void run() {
try {
// TODO: 2020/10/28 执行耗时操作
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+" countDown");
countDownLatch.countDown();
}
});
try {
System.out.println(System.currentTimeMillis()+" await start");
countDownLatch.await(5,TimeUnit.SECONDS);
System.out.println(System.currentTimeMillis()+" await end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
主线程等待5秒,但是子线程任务需要10秒才会完成,所以5秒过后,主线就不在等待。结果如下:
1603891276683 await start
1603891281688 await end
1603891286683 countDown