【258、CountDownLatch你用过吗?能讲讲嘛?以及Semaphore】

71 阅读3分钟

CountDownLatch 是 Java 并发包提供的一个同步工具类,用于控制多个线程之间的同步。

CountDownLatch 具有一个计数器,初始化时设置一个数值,每当一个线程完成了一定的操作,就将计数器减 1。当计数器减至 0 时,表示所有的操作都已完成,可以继续执行后续的操作。通常情况下,主线程会在 CountDownLatch 上等待,直到计数器减至 0,才会继续执行。

CountDownLatch 主要用于等待多个线程完成某个操作之后再继续执行后续操作,比如多个线程都完成了数据的准备工作后再进行数据的处理。

与 CountDownLatch 不同的是,信号量(Semaphore)是另一个 Java 并发包提供的同步工具类,它也可以用于线程之间的同步,但是与 CountDownLatch 不同,信号量可以控制同时访问某个共享资源的线程数量,从而实现对共享资源的保护。

信号量的实现方式是维护一个计数器和一个等待队列,计数器表示当前可访问共享资源的线程数,等待队列保存着正在等待访问共享资源的线程。当一个线程访问共享资源时,信号量会将计数器减 1,当计数器减至 0 时,表示当前已经没有可用的资源了,所有的线程都需要进入等待队列等待。

与 CountDownLatch 相比,信号量的应用场景更加广泛,可以用于控制同时访问共享资源的线程数量、实现线程的优先级控制等。但是,使用信号量需要更加注意对共享资源的保护,防止出现死锁、饥饿等问题。

CountDownLatch 用于等待多个线程完成任务后再继续执行主线程的任务,它的作用类似于一个计数器,主线程等待计数器变为零时再继续执行。CountDownLatch 初始化时需要指定计数器的初始值,每个线程完成任务时调用 countDown() 方法将计数器减一,主线程调用 await() 方法等待计数器变为零。

下面是一个 CountDownLatch 的例子:

public class CountDownLatchExample {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);

        new Thread(() -> {
            System.out.println("Thread 1 is running...");
            latch.countDown();
        }).start();

        new Thread(() -> {
            System.out.println("Thread 2 is running...");
            latch.countDown();
        }).start();

        new Thread(() -> {
            System.out.println("Thread 3 is running...");
            latch.countDown();
        }).start();

        latch.await();
        System.out.println("All threads have finished running.");
    }
}

Semaphore 用于控制同时访问某个资源的线程数,它的作用类似于一个信号量,线程需要先获取信号量才能继续执行,执行完后再释放信号量Semaphore 初始化时需要指定信号量的数量,每个线程需要访问资源时调用 acquire() 方法获取一个信号量,访问完成后调用 release() 方法释放信号量。

下面是一个 Semaphore 的例子:

public class SemaphoreExample {

    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(2);

        new Thread(() -> {
            try {
                semaphore.acquire();
                System.out.println("Thread 1 is running...");
                Thread.sleep(1000);
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                semaphore.acquire();
                System.out.println("Thread 2 is running...");
                Thread.sleep(1000);
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                semaphore.acquire();
                System.out.println("Thread 3 is running...");
                Thread.sleep(1000);
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

从上面的例子可以看出,CountDownLatch 用于等待多个线程完成任务后再继续执行主线程的任务,它的作用类似于一个计数器,主线程等待计数器变为零时再继续执行;而 Semaphore 用于控制同时访问某个资源的线程数,它的作用类似于一个信号量,线程需要先获取信号量才能继续执行,执行完后再释放信号量。