`CountDownLatch` 和 `join()` 方法都是用于在多线程编程中实现线程间的同步。它们有着类似的功能,但是在某些场景下,`CountDownLatch` 相较于 `join()` 具有一些优点:
1. **更灵活的等待条件**:
- `CountDownLatch` 允许主线程等待多个子线程完成任务。主线程可以等待多个线程的同时完成,而不需要一个一个地等待每个线程。
- `join()` 方法只能等待一个线程完成任务,如果有多个线程需要等待,必须分别调用多次 `join()`,使得代码变得冗长。
2. **可复用性**:
- `CountDownLatch` 可以被复用,即在一个计数器归零后,可以重置计数器,再次使用。
- `join()` 方法一旦被调用,就无法被重复使用。如果一个线程已经被 `join()`,再次调用 `join()` 会抛出 `IllegalThreadStateException`。
3. **不影响线程的状态**:
- `join()` 方法会阻塞主线程,直到被等待线程完成或者超时。这可能会影响主线程的执行,特别是在GUI或者服务器程序中,可能导致界面冻结或者响应时间延长。
- `CountDownLatch` 不会阻塞主线程,主线程可以继续执行其他任务,只是在需要等待的时候调用 `await()` 方法等待。
4. **更灵活的任务协调**:
- `CountDownLatch` 允许子线程在完成任务后继续执行其他操作,而不需要等待主线程。这样可以更灵活地安排任务执行的顺序和协作方式。
- `join()` 方法会阻塞主线程,直到被等待线程完成任务后,主线程才能继续执行。这可能会导致线程间的协作受限。
/**
* 作为线程同步使用,除非计数器变成了0 ,否则永远不会停止,可以保证所有的任务都做完 和join的方式类似,但是join是底层的api,用起来过于繁琐
*/
@Slf4j(topic = "c.TestCountDownLatch")
public class TestCountDownLatch {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
ExecutorService service = Executors.newFixedThreadPool(4);
service.submit(()->{
log.debug("begin");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
log.debug("end...{}",latch.getCount());
});
service.submit(()->{
log.debug("begin");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
log.debug("end...{}",latch.getCount());
});
service.submit(()->{
log.debug("begin");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
log.debug("end...{}",latch.getCount());
});
service.submit(()->{
try {
log.debug("waiting");
latch.await();
log.debug("waiting end");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}