Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说CountDownlatch类的介绍和使用,希望能够帮助你!!!。
在日常开发中经常会遇到需要在主线程中开启多个线程去并行执行任务, 并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。在 CountDownLatch 出现之前一 般都使用线程的 join()方法来实现这一点,但是 join 方法不够灵,而CountDownLatch 可以更优雅的实现这种场景应用,而其用起来也更加的灵活。
与join()方法的区别
子线程调用join() 方法后,该线程会一直被阻塞直到子线程运行完毕,而 CountDownLatch 则使用计数器来允许子线程运行完毕或者在运行中递减计数,也就是 CountDownLatch 可以在子线程运行的任何时候让 await 方法返回而不一定必须等到线程结束。另外,使用线程池来管理线程时一般都是直接添加 Runable 到线程池,这时候就没有办法再调用线程的 join 方法了,就是说 countDownLatch 相比 join 方法让我们对线程同步有更灵活的控制。
当线程调用await 方法后,当前线程会被阻塞, 直到下面的情况之一发生才会返回: 当所有线程都调用了 CountDownLatch 对象的 countDown 方法后, 也就是计数器的值为 0 时;其它线程调用了当前线程的 interrupt()方法中断了当前线程,当前线程就会抛出 InterruptedException 异常,然后返回。
当线程调用该方法后, 当前线程会被阻塞,直到下面的情况之一发生才会返回:当所有线程都调用了 CountDownLatch 对象的 countDown 方法后, 也就是计数器值为 0 时,这时候会返回true ;设置的 timeout时间到了,因为超时而返回false;其它线程调用了当前线程的 interrupt()方法中断了当前线程, 当前线程会抛出 Intem1ptedException 异常,然后返回。
线程调用该方法后,计数器的值递减,递减后如果计数器值为0则唤醒所有因调用 await 方法而被阻塞的线程,否则什么都不做。
获取当前计数器的值,一般在测试时使用该方法。
public class CountDownLatchTest {
// 创建一个CountDownLatch实例
private static volatile CountDownLatch cdLatch = new CountDownLatch(3) ;
public static void main(String[] args) throws InterruptedException{
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程1开始运行...");
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
cdLatch.countDown();
System.out.println("线程1运行结束");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程2开始运行...");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cdLatch.countDown();
System.out.println("线程2运行结束");
}
});
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程3开始运行...");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
cdLatch.countDown();
try {
System.out.println("子线程3继续运行...");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程3运行结束");
}
});
//启动子线程
thread1.start() ;
thread2.start() ;
thread3.start() ;
System.out.println("主线程开始等待子线程运行完毕...");
cdLatch.await();
System.out.println("所有子线程执行countDown方法返回!");
}
}
运行结果如下图:
在如上代码中,创建了一个 CountDownLatch 实例对象cdLatch,因为有三个子线程所以构造函数 的传参为3。主线程调用cdLatch.await();方法后会被阻塞。子线程调用 countDown()方法让cdLatch内部的计数器减1,所有子线程调用 countDown()方法后计数器会变为 0,这时候主线程的 await()方法才会返回。从运行结果可以看出线程1和线程2在运行完毕后调用countDown(),而线程3在调用countDown方法后还可以继续运行。
将上述代码改造一下,用ExecutorService线程池来管理多线程运行,代码如下:
public class CountDownLatchTest2 {
// 创建一个CountDownLatch实例
private static volatile CountDownLatch cdLatch = new CountDownLatch(3) ;
public static void main(String[] args) throws InterruptedException{
ExecutorService executorService = Executors.newFixedThreadPool(3) ;
//将线程1添加到线程池
executorService.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程1开始运行...");
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
cdLatch.countDown();
System.out.println("线程1运行结束");
}
});
//将线程2添加到线程池
executorService.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程2开始运行...");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cdLatch.countDown();
System.out.println("线程2运行结束");
}
});
//将线程3添加到线程池
executorService.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程3开始运行...");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
cdLatch.countDown();
try {
Thread.sleep(1000);
System.out.println("子线程3继续运行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程3运行结束");
}
});
System.out.println("主线程开始等待子线程运行完毕...");
cdLatch.await();
System.out.println("所有子线程执行countDown方法返回!");
executorService.shutdown();//关闭线程池
}
}
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
上一篇
已是最后文章
下一篇
已是最新文章