CountDownLatch是《java并发编程实战》这本书里讲的同步工具类的第一种,闭锁的一种实现。直接先上模拟代码:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
FirstService t1 = new FirstService(countDownLatch);
SecondService t2 = new SecondService(countDownLatch);
Long start = System.currentTimeMillis();
t1.start();
t2.start();
countDownLatch.await();
Long end = System.currentTimeMillis();
System.out.println("两个服务启动耗时:" + (end - start));
}
}
class FirstService extends Thread {
CountDownLatch countDownLatch;
public FirstService(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
// synchronized (countDownLatch) {
try {
System.out.println("第一个服务准备启动......");
Thread.sleep(2000);
System.out.println("第一个服务正在启动......");
Thread.sleep(2000);
System.out.println("第一个服务启动完毕......");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();
}
// }
}
}
class SecondService extends Thread {
CountDownLatch countDownLatch;
public SecondService(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
// synchronized (countDownLatch) {
try {
System.out.println("第二个服务准备启动......");
Thread.sleep(3000);
System.out.println("第二个服务正在启动......");
Thread.sleep(3000);
System.out.println("第二个服务启动完毕......");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();
}
// }
}
}
这里模拟两个线程代表两个服务,main方法需要等这两个线程都执行完毕之后才进行一些操作(这里是统计服务启动时间)。所以我们使用CountDownLatch类来进行记录。
初始CountDownLatch countDownLatch = new CountDownLatch(2);
创建一个值为2的CountDownLatch对象,并让两个线程共享这个对象。
主线程里调用countDownLatch.await();
方法后进入阻塞,两个线程执行完毕后执行countDownLatch.countDown();
方法,会将CountDownLatch里的值减1,当CountDownLatch值为0的时候,主线程的阻塞则会结束,继续运行。
await()和countDown()就是常用的两个方法。
上面代码输出如下,主线程在两个线程都执行完毕后再继续执行:
第二个服务准备启动......
第一个服务准备启动......
第一个服务正在启动......
第二个服务正在启动......
第一个服务启动完毕......
第二个服务启动完毕......
两个服务启动耗时:6005
我第一次看到这个方法的时候,感觉就效果来说跟Thread类的join()方法很像。比如我在主线程里new一个线程t1,然后调用t1.join()方法,主线程就会阻塞,直到线程t1结束后才继续进行。
所以上面这段代码,用join也能实现,分别对两个线程调用join方法就行。
但是join方法是必须等到线程完全执行完才会回到调用线程的执行逻辑,CountDownLatch则不用,你可以在线程执行的过程中就调用countDown方法,比如下面这段代码:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
FirstService1 t1 = new FirstService1(countDownLatch);
SecondService1 t2 = new SecondService1(countDownLatch);
t1.start();
t2.start();
countDownLatch.await();
System.out.println("两个服务第一阶段全部启动完毕");
}
}
class FirstService1 extends Thread {
CountDownLatch countDownLatch;
public FirstService1(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
System.out.println("第一个服务第一阶段开始......");
Thread.sleep(2000);
countDownLatch.countDown();
System.out.println("第一个服务第二阶段开始......");
Thread.sleep(2000);
System.out.println("第一个服务启动完毕......");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
}
}
}
class SecondService1 extends Thread {
CountDownLatch countDownLatch;
public SecondService1(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
System.out.println("第二个服务第一阶段开始......");
Thread.sleep(3000);
countDownLatch.countDown();
System.out.println("第二个服务第二阶段开始......");
Thread.sleep(3000);
System.out.println("第二个服务启动完毕......");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
}
}
}
这段代码模拟两个服务,启动分两个阶段,第一阶段都执行结束之后就可以执行主线程逻辑了。于是我们把countDown方法放到线程中执行,这样在第一阶段结束后,countDownLatch对象的值就为0了,主线程的阻塞就解除了。
代码运行结果如下:
第二个服务第一阶段开始......
第一个服务第一阶段开始......
第一个服务第二阶段开始......
第二个服务第二阶段开始......
两个服务第一阶段全部启动完毕
第一个服务启动完毕......
第二个服务启动完毕......
所以使用CountDownLatch类,要比join方法灵活。
参考:
https://www.jianshu.com/p/795151ac271b
感谢大佬带飞