蚂蚁一面被问到这个问题,当时实在是想不到两者在实现相同功能时的区别,回来百度了一下,发现其实还是蛮简单的问题哈。
首先,两者都能够实现阻塞线程等待完成后,再继续进行后续逻辑,对于两者相同的功能这里不再赘述,我们直接说说它们之间的区别,考虑一个场景,我们的主线程阻塞到某处,等待其它线程完成某些操作之后再继续后续操作,具体就是主线程M,等待子线程T1和T2完成某项操作,但是子线程T1和T2中的任务可能有很多事情要做,而其中只有中间某一步完成后,其实就可以唤醒主线程继续执行,而不需要等待子线程所有任务执行完毕再执行主线程。这种情况下,如果通过调用子线程的join
方法来卡住主线程,那么主线程就必须等待子线程所有任务全部执行完毕,才能继续执行,这种情况下效率是极其低下甚至不可行的,此时就必须要用CountDownLatch
来实现更加细粒度的任务控制,示例代码如下:
- Worker.java
public class Worker extends Thread {
private String name;
private long time;
private CountDownLatch countDownLatch;
public Worker(String name, long time, CountDownLatch countDownLatch) {
this.name = name;
this.time = time;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
Thread.sleep(time);
System.out.println(name+"第一阶段工作完成");
countDownLatch.countDown();
Thread.sleep(2000); //这里就姑且假设第二阶段工作都是要2秒完成
System.out.println(name+"第二阶段工作完成");
System.out.println(name+"工作完成,耗费时间="+(time+2000));
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
- Test.java
public class Test {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
Worker worker0 = new Worker("worker0", (long) (Math.random()*2000+3000), countDownLatch);
Worker worker1 = new Worker("worker1", (long) (Math.random()*2000+3000), countDownLatch);
Worker worker2 = new Worker("worker2", (long) (Math.random()*2000+3000), countDownLatch);
worker0.start();
worker1.start();
countDownLatch.await();
System.out.println("准备工作就绪");
worker2.start();
}
}
以上代码work0
和work1
只需要执行完第一阶段的任务主线程work2
就可以开始执行了,如果用join
的话,就必须要等待work0
和work1
的所有任务执行完毕,work2
就需要无故等待两秒中再执行,所以显然此时用CountDownLatch
是更加合适的。
参考:https://blog.csdn.net/nyistzp/article/details/51444487