文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java 并发特性之 CountDownLatch 详解!

2024-11-28 14:52

关注

一、什么是 CountDownLatch?

CountDownLatch是java.util.concurrent 包的一部分,用于同步一个或多个线程以等待特定条件的满足。它在创建时初始化一个给定的计数,表示必须发生的事件数量,才能使线程继续执行。这个计数通过调用 countDown() 方法来递减,等待该条件的线程调用 await() 方法来阻塞,直到计数达到零。

CountDownLatch的关键组件包含:

二、工作原理

CountDownLatch 本质上是一种简化的信号量(Semaphore)。它的核心思想是设定一个计数器,当计数器值为 0 时,其他被阻塞的线程才会开始运行,线程的释放建立在调用 countDown 方法去减少计数器次数的基础上。

CountDownLatch 的典型功能包括:

它包含了两个核心方法:

通过这两种操作,我们就可以构建出各种灵活的并发控制逻辑。

1.简单实现

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个计数器,设置初始的计数值为 3
        CountDownLatch latch = new CountDownLatch(3);

        // 创建三个工作线程
        new Thread(() -> {
            try {
                Thread.sleep(1000);  // 模拟任务耗时
                System.out.println("Thread 1 finished");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();  // 每个线程任务完成后,使计数器减 1
            }
        }).start();

        new Thread(() -> {
            try {
                Thread.sleep(2000);  // 模拟任务耗时
                System.out.println("Thread 2 finished");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();  // 每个线程任务完成后,使计数器减 1
            }
        }).start();

        new Thread(() -> {
            try {
                Thread.sleep(3000);  // 模拟任务耗时
                System.out.println("Thread 3 finished");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();  // 每个线程任务完成后,使计数器减 1
            }
        }).start();

        // 主线程会在此阻塞,直到计数器减为 0
        latch.await();
        System.out.println("All tasks finished. Main thread proceeding.");
    }
}

输出结果如下:

Thread 1 finished
Thread 2 finished
Thread 3 finished
All tasks finished. Main thread proceeding.

在上述代码中,主线程调用 latch.await(); 进入阻塞状态,等待三个工作线程完成任务后,计数器将变为 0,然后解除阻塞并进入后续逻辑。

2.底层工作原理

从底层工作原理来看,CountDownLatch 内部维护了一个 Sync 类,这实际上是一个基于 AQS(AbstractQueuedSynchronizer, 抽象队列同步器)的同步工具。

内部实现机制对线程的阻塞、唤醒、队列管理等是通过 AQS 实现的,AQS 的设计模式使得它能高效、安全地管理同步状态。

3.核心代码片段

static final class Sync extends AbstractQueuedSynchronizer {
    Sync(int count) {
        setState(count);
    }

    int getCount() {
        return getState();
    }

    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }

    protected boolean tryReleaseShared(int releases) {
        // Decrement count; signal when transition to zero
        for (;;) {
            int c = getState();
            if (c == 0)
                return false;
            int nextc = c - 1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
}

三、使用场景分析

CountDownLatch 的应用场景比较广泛,尤其是在处理并发问题时,这里列举了几个:

1.批量任务协调

有时候,不同子线程可能会同时执行各自的任务,然而主线程会等待所有子线程的执行完毕后,才继续执行后续操作。

比如 Web 应用中多个 API 的响应聚合:假设有多个远程服务需要调用,主线程希望在所有调用都返回结果后,再执行后续处理,可以使用 CountDownLatch 来等待响应的到来。示例代码如下:

// 类似一些应用需要同时从多个微服务中拉数据,再一起处理
CountDownLatch latch = new CountDownLatch(3);
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
    executor.submit(() -> {
        try {
            // 模拟拉取和处理数据
        } catch (Exception e) {
            // 异常处理
        } finally {
            latch.countDown(); // 每个任务结束后调用
        }
    });
}
latch.await();  // 等待所有子任务执行结束
System.out.println("汇总所有数据.");

2.并行计算

假如有这样一个情景:计算任务很耗时,但是可以分成多个部分并行处理,然后将结果进行合并。

实现方式:我们先将任务分解成 n 个子任务,全部执行完毕后,将子任务的结果进行汇总分析。示例代码如下:

CountDownLatch latch = new CountDownLatch(n);
List results = new CopyOnWriteArrayList<>();
for (int i = 0; i < n; i++) {
    new Thread(() -> {
        try {
            int result = // 处理部分任务
            results.add(result);
        } finally {
            latch.countDown();  // 完成后计数减 1
        }
    }).start();
}
latch.await();  // 等到结果全部处理完
int finalResult = results.stream().mapToInt(Integer::intValue).sum();

3.服务启动检查

CountDownLatch 还可以为应用服务做“健康检查”。例如,系统在完全启动之前,需要依赖多个外部服务,那么我们可以通过异步方式检测各个服务的健康状态,只有当所有服务都正常启动时,才允许继续执行下一步。

简单的示例代码如下:

public class ServiceStartChecker {

    private final CountDownLatch latch;

    public ServiceStartChecker(int serviceCount) {
        latch = new CountDownLatch(serviceCount);
    }

    public void checkServices() throws InterruptedException {
        // 启动多个异步线程去检查服务是否就绪
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    // 模拟服务检查
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " is ready");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown(); // 完成后调用,减少计数器
                }
            }).start();
        }
        latch.await(); // 等待所有服务就绪
        System.out.println("All services are up. System is ready.");
    }

    public static void main(String[] args) throws InterruptedException {
        ServiceStartChecker checker = new ServiceStartChecker(3);
        checker.checkServices();
    }
}

四、与其他并发工具对比

CountDownLatch 只是 Java 并发工具包中的一个工具,其功能与一些其他工具如 CyclicBarrier、Semaphore 等具有一定的共性和不同点。

1.CountDownLatch vs CyclicBarrier

2.CountDownLatch vs Semaphore

任务控制力度的差异:Semaphore 更倾向于对信号量的数量进行限流。简单来说,Semaphore 可以限制某个操作的并发次数,比如最多只允许 5 个线程同时执行某个任务。而 CountDownLatch 只是简单的减少计数,不去限流,只是关注完成情况。

五、实际项目中的使用

在多线程爬虫、分布式系统、并行数据处理等具体项目中,CountDownLatch 都能找到合适的应用场景。

1.分布式系统的启动控制

假设我们在一个分布式服务系统中,每个微服务间可能有复杂的依赖关系,借助 CountDownLatch,我可以构建出一个依赖的启动顺序。

2.性能测试

在进行性能测试时,可能需要多个线程同时工作,例如使用 CountDownLatch 控制开始时间,以模拟高并发访问场景。

CountDownLatch ready = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(N);
for (int i = 0; i < N; i++) {
    new Thread(() -> {
        try {
            ready.await();  // 等待所有线程就绪
            // 执行模拟请求
        } finally {
            done.countDown();
        }
    }).start();
}
// 开始测试
ready.countDown();
done.await();  // 等到所有线程结束

通过这样的实践,我们可以轻松模拟高并发性能测试和压力测试场景。

总结

本文我们深度剖析了CountDownLatch,CountDownLatch虽然是一个简单的并发工具,对其整体总结如下:

来源:猿java内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯