CountdownLatch简介

CountDownLatch作用

CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能,CountDownLatch可以用来进行线程同步协作,等待所有线程完成倒计时。CountDownLatch内部会维护一个初始值为线程数量的计数器,主线程执行await方法,如果计数器大于0,则阻塞等待。当一个线程完成任务后,计数器值减1。当计数器为0时,表示所有的线程已经完成任务,等待的主线程被唤醒继续执行。
image.png

主要方法

  1. //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
  2. await();
  3. //作用同上面方法,只是增加了超时限制
  4. await( longtimeout, TimeUnit unit);
  5. //将维护的count值减1
  6. countDown();

CountdownLatch的使用

效果演示

  1. public static void m1() throws InterruptedException {
  2. CountDownLatch latch = new CountDownLatch(3);
  3. new Thread(() -> {
  4. log.debug("begin...");
  5. try {
  6. Thread.sleep(1000);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. latch.countDown();
  11. log.debug("end...{}", latch.getCount());
  12. }).start();
  13. new Thread(() -> {
  14. log.debug("begin...");
  15. try {
  16. Thread.sleep(2000);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. latch.countDown();
  21. log.debug("end...{}", latch.getCount());
  22. }).start();
  23. new Thread(() -> {
  24. log.debug("begin...");
  25. try {
  26. Thread.sleep(1500);
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. }
  30. latch.countDown();
  31. log.debug("end...{}", latch.getCount());
  32. }).start();
  33. log.debug("waiting...");
  34. latch.await();
  35. log.debug("wait end...");
  36. }

运行这个方法,运行结果:
image.png
可以看到main线程中调用“latch.await()”确实在等待计数器清零才继续运行。从运行效果来看,这个CountDownLatch的方法似乎和Thread类的join方法没有什么区别,但是如果考虑线程池的情况:固定线程池中的非救急线程是不会停止运行的,他会一直处于活跃状态,因此如果使用join方法就不行了,这时候就体现了计数器的强大之处了,如下面代码:

  1. public static void m2() {
  2. CountDownLatch latch = new CountDownLatch(3);
  3. ExecutorService service = Executors.newFixedThreadPool(4);
  4. service.submit(() -> {
  5. log.debug("begin...");
  6. try {
  7. Thread.sleep(1000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. latch.countDown();
  12. log.debug("end...{}", latch.getCount());
  13. });
  14. service.submit(() -> {
  15. log.debug("begin...");
  16. try {
  17. Thread.sleep(1500);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. latch.countDown();
  22. log.debug("end...{}", latch.getCount());
  23. });
  24. service.submit(() -> {
  25. log.debug("begin...");
  26. try {
  27. Thread.sleep(2000);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. latch.countDown();
  32. log.debug("end...{}", latch.getCount());
  33. });
  34. //用于提交任务的线程
  35. service.submit(() -> {
  36. try {
  37. log.debug("waiting...");
  38. latch.await();
  39. log.debug("wait end...");
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. }
  43. });
  44. }

image.png

加载游戏小案例

  1. public static void m3() throws InterruptedException {
  2. AtomicInteger num = new AtomicInteger(0);
  3. ExecutorService service = Executors.newFixedThreadPool(10, (r) -> {
  4. return new Thread(r, "t" + num.getAndIncrement());
  5. });
  6. CountDownLatch latch = new CountDownLatch(10);
  7. String[] all = new String[10];
  8. Random r = new Random();
  9. for (int j = 0; j < 10; j++) {
  10. int x = j;
  11. service.submit(() -> {
  12. for (int i = 0; i <= 100; i++) {
  13. try {
  14. Thread.sleep(r.nextInt(100));
  15. } catch (InterruptedException e) {
  16. }
  17. all[x] = Thread.currentThread().getName() + "(" + (i + "%") + ")";
  18. System.out.print("\r" + Arrays.toString(all));
  19. }
  20. latch.countDown();
  21. });
  22. }
  23. latch.await();
  24. System.out.println("\n游戏开始...");
  25. service.shutdown();
  26. }

image.png
image.png