CountDownLatch的详解以及用法示例

发布时间:2024年01月17日


前言

在本教程中,我们将详细讲解 CountDownLatch 的使用方法和原理,并通过清晰的语言和示例代码展示其灵活性和强大功能。首先,我们会对 CountDownLatch 进行简单的概述,了解其基本原理和适用场景。接着,我们会提供详细的步骤和示例来演示如何正确地使用 CountDownLatch。我们还会讨论一些注意事项和常见问题,以帮助小伙伴们更好地应用 CountDownLatch。

无论你是新手还是有经验的开发者,本教程都将为你提供有用的信息和示例,使你能够更好地理解和应用 CountDownLatch。希望这篇教程能够为你提供帮助,并使你在并发编程中更加得心应手。

现在,让我们开始深入探索 CountDownLatch 的世界吧!


一、CountDownLatch原理

CountDownLatch 是基于 AQS(AbstractQueuedSynchronizer)实现的一种多线程同步工具。AQS 是 Java 并发包中的一个基础同步框架,提供了实现锁和同步器的基本功能。

在 CountDownLatch 内部,它使用一个同步器 Sync 来管理计数器和等待线程。CountDownLatch 维护着一个初始值为 N 的计数器,其中 N 表示需要等待的线程数量。

当一个线程通过调用 countDown() 方法来完成某个操作时,计数器会减少1。同时,Sync 中的 tryReleaseShared() 方法会被调用,该方法将计数器减少后返回新的计数器值。如果新的计数器值为0,表示所有线程都已经完成任务,此时 tryReleaseShared() 方法会返回一个非负数(通常是1),唤醒所有在等待队列中的线程,使它们可以继续执行。

当一个线程调用 await() 方法时,Sync 中的 tryAcquireShared() 方法会被调用,该方法会检查计数器的值。如果计数器不为0,当前线程将进入等待队列,并通过自旋等待计数器变为零。一旦计数器为0,tryAcquireShared() 方法会返回一个非负数(通常是1),将当前线程从等待队列中释放出来,使其可以继续执行后续操作。

通过计数器和同步器的配合,CountDownLatch 实现了一个非常简单而有效的线程同步机制。主线程可以调用 await() 方法来等待所有工作线程执行完成,而工作线程在完成任务后调用 countDown() 方法来减少计数器的值。当计数器变为零时,主线程被唤醒并继续执行后续操作。

CountDownLatch 的设计使得它可以应用于多种场景,例如王者荣耀这个例子,我们可以使用 CountDownLatch 来等待所有玩家加载游戏后再开始比赛。每个玩家加载完毕后,他们会调用 countDown() 方法来减少计数器的值。主线程(或服务器)通过调用 await() 方法来等待计数器变为零。
当计数器的值变为零时,意味着所有玩家都加载完成了,主线程可以继续执行后续操作,比如开始游戏。通过使用 CountDownLatch,我们能够实现并行加载游戏,而不需要等待其他玩家完成。只有当所有玩家都加载完成时,主线程才会开始游戏,从而提高了整体游戏的效率和流畅度。

二、代码示例及讲解

1.代码如下(示例):

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        int number = 3;
        CountDownLatch latch = new CountDownLatch(number);
        for (int i = 0; i < number; i++) {
            Thread workerThread = new Thread(new WorkerTask(latch));
            workerThread.start();
        }
        // 等待所有子任务完成
        latch.await();
        System.out.println("所有子任务已完成,开始执行主任务");
    }
}

class WorkerTask implements Runnable {
    private final CountDownLatch latch;

    public WorkerTask(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            // 模拟子任务的耗时操作
            Thread.sleep((long) (Math.random() * 2000));
            System.out.println("子任务完成: " + Thread.currentThread().getName());
            // 子任务完成后调用 countDown 方法
            latch.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.运行结果

在这里插入图片描述

3.代码详解

首先,在 ThreadTest 类中,我们创建了一个名为 latch 的 CountDownLatch 对象,初始值为 3。这意味着需要等待三个子任务完成后,主任务才能继续执行。

然后,在 for 循环中,我们创建了3个线程,并将每个线程绑定到一个 WorkerTask 实例上。WorkerTask 是一个实现了 Runnable 接口的类,它接收 CountDownLatch 对象作为构造参数。

在 WorkerTask 的 run 方法中,我们模拟了子任务的耗时操作,通过调用 Thread.sleep 方法来使当前线程休眠一段时间。然后,输出当前线程的名称和提示信息,表示子任务完成。

最后,在子任务完成后,调用 latch.countDown() 方法来减少计数器的值。这告诉 CountDownLatch 一个子任务已经完成。

在主线程中,我们调用了 latch.await() 方法来等待所有子任务完成。当计数器的值变为 0 时,主线程将继续执行,并输出"所有子任务已完成,开始执行主任务"。

通过使用 CountDownLatch,我们可以很容易地实现并发编程中的线程同步,确保在主任务中只有在所有子任务完成后才会执行。


总结

通过本文对 CountDownLatch 的讲解,我们了解到它是一个用于线程同步的工具类。使用 CountDownLatch 可以实现等待其他线程完成任务后再继续执行的功能。但是在使用 CountDownLatch 时,需要注意以下几点:

  • 创建 CountDownLatch 对象时,需指定初始计数值。
  • 在每个等待的线程中调用 countDown() 方法来减少计数器的值。
  • 主线程(或其他线程)使用 await() 方法阻塞等待计数器变为零,表示所有线程都已完成任务。

适当地应用 CountDownLatch 可以优化并发编程场景,确保线程之间的同步和顺序执行。

希望大家在实战中能够灵活运用 CountDownLatch,进一步提升多线程编程的效率和质量。

感谢阅读本文,如果还有其他问题,请随时提问。

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