原创

java面试题-说说你对CountDownLatch的理解

引言

在多线程编程中,实现线程之间的协调和通信是至关重要的。CountDownLatch是Java中提供的一个强大工具,用于协调多个线程的执行。本教程将深入探讨CountDownLatch的概念、用法以及示例代码,旨在帮助初学者更好地理解和运用这一同步工具。

1. CountDownLatch的概念

CountDownLatch是一个同步工具类,主要用于协调多个线程之间的同步。它并不是用来实现互斥访问的,而是用于线程之间的通信。其核心思想是通过一个计数器来实现线程间的等待和通知。

CountDownLatch的作用是允许一个或多个线程等待其他线程完成操作,然后再继续执行。

2. CountDownLatch的基本用法

CountDownLatch典型用法包括:

  • 主线程等待其他线程执行完成再执行;
  • 实现多个线程开始执行任务的最大并行性。

示例1:主线程等待子线程执行完成再执行

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountdownLatchTest1 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(3);
        final CountDownLatch latch = new CountDownLatch(3);

        for (int i = 0; i < 3; i++) {
            Runnable runnable = () -> {
                try {
                    System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println("子线程" + Thread.currentThread().getName() + "执行完成");
                    latch.countDown(); // 当前线程调用此方法,则计数减一
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            service.execute(runnable);
        }

        try {
            System.out.println("主线程" + Thread.currentThread().getName() + "等待子线程执行完成...");
            latch.await(); // 阻塞当前线程,直到计数器的值为0
            System.out.println("主线程" + Thread.currentThread().getName() + "开始执行...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        service.shutdown();
    }
}

解释示例1:

  • 主线程创建了一个固定大小的线程池,初始化CountDownLatch计数器为3。
  • 子线程在执行时,会调用latch.countDown()将计数器减一。
  • 主线程通过latch.await()等待计数器为0,即等待所有子线程执行完成。
  • 一旦计数器为0,主线程继续执行。

示例2:百米赛跑

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountdownLatchTest2 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final CountDownLatch cdOrder = new CountDownLatch(1);
        final CountDownLatch cdAnswer = new CountDownLatch(4);

        for (int i = 0; i < 4; i++) {
            Runnable runnable = () -> {
                try {
                    System.out.println("选手" + Thread.currentThread().getName() + "正在等待裁判发布口令");
                    cdOrder.await();
                    System.out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println("选手" + Thread.currentThread().getName() + "到达终点");
                    cdAnswer.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            service.execute(runnable);
        }

        try {
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("裁判" + Thread.currentThread().getName() + "即将发布口令");
            cdOrder.countDown();
            System.out.println("裁判" + Thread.currentThread().getName() + "已发送口令,正在等待所有选手到达终点");
            cdAnswer.await();
            System.out.println("所有选手都到达终点");
            System.out.println("裁判" + Thread.currentThread().getName() + "汇总成绩排名");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        service.shutdown();
    }
}

解释示例2:

  • 选手线程等待裁判口令,裁判通过cdOrder.countDown()发布口令。
  • 选手线程通过cdAnswer.await()等待所有选手到达终点,裁判通过cdAnswer.countDown()通知所有选手到达。
  • 裁判线程在所有选手到达后进行汇总。

3. CountDownLatch的不足

CountDownLatch是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值。当CountDownLatch使用完毕后,它不能再次被使用。

4. CountDownLatch使用说明

方法说明

public void countDown()

递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则将计数减少。

public boolean await(long timeout, TimeUnit unit) throws InterruptedException

使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回true值。

如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直处于休眠状态:

  1. 由于调用countDown()方法,计数到达零;
  2. 或者其他某个线程中断当前线程;
  3. 或者已超出指定的等待时间。

如果计数到达零

,则该方法返回true值。

如果当前线程:

  • 在进入此方法时已经设置了该线程的中断状态;
  • 或者在等待时被中断,

则抛出InterruptedException,并且清除当前线程的已中断状态。

如果超出了指定的等待时间,则返回值为false。如果该时间小于等于零,则该方法根本不会等待。

参数

  • timeout: 要等待的最长时间。
  • unit: timeout参数的时间单位。

返回值

  • 如果计数到达零,则返回true;如果在计数到达零之前超过了等待时间,则返回false。

异常

  • InterruptedException: 如果当前线程在等待时被中断。

5. 总结

CountDownLatch是一个强大的多线程同步工具,通过它可以实现线程之间的协调和通信。本教程通过详细的示例代码演示了CountDownLatch的基本用法,包括主线程等待子线程执行完成再执行和百米赛跑的场景。了解CountDownLatch的不足和使用说明,帮助初学者更好地理解和运用这一同步工具。

正文到此结束
本文目录