Java中CountDownLatch的深入解析与实践

发布时间:2024年01月01日

Java中CountDownLatch的深入解析与实践

引言

在多线程编程中,协调各个线程的启动、完成通常是一项挑战。Java的并发API提供了多种同步辅助工具来简化这种协调,其中之一就是CountDownLatch。本文将详细介绍CountDownLatch的概念、使用方法,并通过实例来加深理解。

什么是CountDownLatch?

CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。简单来说,它是一个计数器,但它的特性是,它只能倒数,不能正数。

当我们创建一个CountDownLatch的实例时,我们指定一个整数作为计数器的初始值。每次调用countDown()方法时,计数器的值会减1。当计数器的值减到0时,所有因await()方法等待的线程将被唤醒并继续执行。

CountDownLatch的基本用法

在深入示例之前,让我们先看一下CountDownLatch的基本用法。它主要涉及三个操作:

  1. 初始化计数器:CountDownLatch latch = new CountDownLatch(N); 其中N是你希望等待的操作数。
  2. 等待计数器达到零:latch.await(); 任何调用此方法的线程都将被阻塞,直到计数器值为零。
  3. 计数器值减一:latch.countDown(); 每次此方法被调用,计数器的值就会减一。

CountDownLatch的实例应用

示例1:等待多个服务启动

假设我们有一个应用程序,它依赖于多个外部服务。在应用程序可以启动之前,所有这些外部服务都需要先启动。我们可以使用CountDownLatch来确保这一点。

public class ServiceLauncher {
    private final CountDownLatch latch;

    public ServiceLauncher(int numberOfServices) {
        latch = new CountDownLatch(numberOfServices);
    }

    public void launchService(Runnable service) {
        new Thread(() -> {
            service.run();
            latch.countDown();
        }).start();
    }

    public void awaitServices() throws InterruptedException {
        latch.await();
        System.out.println("所有服务已启动,应用程序可以继续!");
    }

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

        launcher.launchService(() -> {
            System.out.println("服务1启动中...");
            // 模拟服务启动耗时
            try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("服务1已启动");
        });

        launcher.launchService(() -> {
            System.out.println("服务2启动中...");
            try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("服务2已启动");
        });

        launcher.launchService(() -> {
            System.out.println("服务3启动中...");
            try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("服务3已启动");
        });

        launcher.awaitServices();
    }
}

在这个例子中,我们创建了一个ServiceLauncher类,它使用CountDownLatch来确保所有服务都启动后应用程序才继续执行。每个服务启动后都会调用countDown()来减少计数器的值。

示例2:等待多个任务完成

另一个常见用例是在主线程中等待一组任务完成。例如,我们可能想要并行处理一组数据,然后在所有处理完成后继续执行。

public class TaskProcessor {
    private final CountDownLatch latch;

    public TaskProcessor(int numberOfTasks) {
        latch = new CountDownLatch(numberOfTasks);
    }

    public void processTask(int taskId) {
        new Thread(() -> {
            System.out.println("任务 " + taskId + " 正在执行...");
            // 模拟任务执行耗时
            try { Thread.sleep((long)(Math.random() * 2000)); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("任务 " + taskId + " 完成");
            latch.countDown();
        }).start();
    }

    public void awaitCompletion() throws InterruptedException {
        latch.await();
        System.out.println("所有任务执行完毕!");
    }

    public static void main(String[] args) throws InterruptedException {
        TaskProcessor processor = new TaskProcessor(5);

        for (int i = 1; i <= 5; i++) {
            processor.processTask(i);
        }

        processor.awaitCompletion();
    }
}

在这个例子中,我们创建了一个TaskProcessor类,它使用CountDownLatch来确保所有任务都完成后程序才继续执行。每个任务完成后都会调用countDown()来减少计数器的值。

CountDownLatch的注意事项

  • CountDownLatch的计数器无法被重置,如果需要能够重置计数的版本,可以考虑使用CyclicBarrier
  • await()方法会使当前线程等待,直到计数器达到零,但也可以指定等待时间,超时后线程将不再等待。
  • CountDownLatch是线程安全的,内部实现确保了计数器的正确递减和线程的安全唤醒。

结语

CountDownLatch是Java并发工具箱中的一个非常有用的组件,它能够简化线程间的协作,确保在继续执行前特定的条件得到满足。通过上述示例,我们了解了它的基本使用方法和一些实际应用场景。在实际开发中,合理利用CountDownLatch可以帮助我们更好地控制并发流程,提高程序的健壮性和用户体验。

文章来源:https://blog.csdn.net/fudaihb/article/details/135263108
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。