CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。
可以阻塞其他进程。使得人为的 控制某些进程结束后,其他进程再得以继续。
例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。
用法介绍
CountDownLatch相当于一个计数器。用一个给定的数值初始化CountDownLatch之后,数器就从这个值开始倒计数,直到计数值达到零 。CountDownLatch 是通过“共享”实现的。在创建 CountDownLatch 时, 会传递一个 int 类型参数,该参数是“计数器”的初始状态,表示该“共享” 最 多 能 被 count 个 线 程 同 时 获 取 , 这 个 值 只 能 被 设 置 一 次 , 而 且 CountDownLatch 没有提供任何机制去重新设置这个计数值。主线程必须在启 动其他线程后立即调用 await()方法。这样主线程的操作就会在这个方法上阻 塞,直到其他线程完成各自的任务。当某线程调用该 CountDownLatch 对象的 await()方法时,该线程会等待“共享”可用时,才能获取“共享”进而继 续运行。而“共享”可用的条件,就是“计数器”的值为 0!而“计数器” 的初始值为 count,每当一个线程调用该 CountDownLatch 对象的 countDown() 方法时,才将“计数器”-1;通过这种方式,必须有 count 个线程调用 countDown()之后,“计数器”才为 0,而前面提到的等待线程才能继续运行!
package com.grace527.test;
import java.util.concurrent.CountDownLatch;
public class CDLTest{
static CountDownLatch latch = new CountDownLatch(2);
static Integer sleeptime = 1000;
/**@Title: main
* @Description: CDLTest测试主函数
* @ReturnType: void
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
TestWorker t1 = new TestWorker("程序员1", latch);
TestWorker t2 = new TestWorker("程序员2", latch);
TestWorker t3 = new TestWorker("程序员3", latch);
TestWorker t4 = new TestWorker("程序员4", latch);
t1.start();
t2.start();
t3.start();
t4.start();
latch.await(); //这里是为了检验其是否阻塞了后两个线程的进行,所以放在了四个线程的后面,实际使用的时候应当是放在需要被阻塞的线程之前
System.out.println("主线程结束运行!~");
}
static class TestWorker extends Thread{
private String workname;
private CountDownLatch latch;
public TestWorker(String workname, CountDownLatch latch) {
super();
this.workname = workname;
this.latch = latch;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
sleep(1000L);
System.out.println("workname:"+workname +"开始工作了!");
//为了看出差别,每个线程运行的时间依次增加1s
sleeptime = sleeptime+1000;
sleep(sleeptime);
System.out.println("workname:"+workname +"停止工作了!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
latch.countDown();
}
}
}
运行结果:
package com.grace527.test;
import java.util.concurrent.CountDownLatch;
public class Test {
static CountDownLatch latch = new CountDownLatch(2);
static Integer sleeptime = 7000;
/**@Title: main
* @Description:
* @ReturnType: void
* @param args
* @throws InterruptedException
*/
/**@Title: main
* @Description:
* @ReturnType: void
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
TestWorker t1 = new TestWorker("程序员1", latch,1000l);
TestWorker t2 = new TestWorker("程序员2", latch,2000L);
TestWorker t3 = new TestWorker("程序员3", latch,3000L);
TestWorker t4 = new TestWorker("程序员4", latch,4000L);
TestWorker t5 = new TestWorker("程序员5", latch,5000L);
TestWorker t6 = new TestWorker("程序员6", latch,6000L);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
latch.await();
System.out.println("主线程结束运行!~");
}
static class TestWorker extends Thread{
private String workname;
private CountDownLatch latch;
private Long sleeptime;
public TestWorker(String workname, CountDownLatch latch,Long sleeptime) {
super();
this.workname = workname;
this.latch = latch;
this.sleeptime = sleeptime;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
sleep(1000L);
System.out.println("workname:"+workname +"开始工作了!");
//sleeptime = sleeptime-1000;
sleep(this.sleeptime);
System.out.println("workname:"+workname +"停止工作了!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//添加条件,使得不是所有线程的执行都会修改计数器
if(sleeptime%2000==0)
latch.countDown();
}
}
}
稍微修改一下代码,会发现,能成功阻塞的线程应当是在await(),之后的线程,且阻塞的条件在CountDownLatch 的计数器变为0 的时候会马上被破坏。
方法详解:
- 列await()函数的作用是让线程阻塞等待其他线程,直到 CountDownLatch 的计 数值变为 0,才继续执行之后的操作。
- countDown()函数:这个函数用来将 CountDownLatch 的计数值减 1,如果计 数达到 0,则释放所有等待的线程。 它的应用场景: 一个任务,它需要等待其他的一些任务都执行完毕之后它才能继续执行。 比如:开 5 个多线程去下载,当 5 个线程都执行完了才算下载成功
每次运行结果的线程编号不同的原因是JVM对线程启动的不确定原因,但是可以看出,每次在回到主线程之前,都是在执行完两个线程之后,也就是CountDownLatch计数器为0之后。