详解Java线程同步器CountDownLatch

详解Java线程同步器CountDownLatch

概述

CountDownLatch是Java的一个线程同步器,用途是让一些线程等待直到另一些线程完成一系列操作。它可以让我们控制一个线程在其他一些线程都完成后才开始执行,如保证某些共享变量在多个线程修改后再执行后续操作。

CountDownLatch是通过一个计数器来实现的,计数器初始值为指定的值,每当一个线程完成了指定的操作后,计数器的值就减1,当计数器值为0时,则所有在这个CountDownLatch上等待的线程开始执行。

示例

下面,我们来看两个示例,分别说明如何使用CountDownLatch。

示例一

假设我们有一个需求,要求计算40000个数字的和。为了提高效率,我们可以将这些数字分配给4个线程,并通过CountDownLatch来控制这4个线程全部计算完成后,再将计算结果求和。

我们的实现代码如下:

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

public class CountDownLatchDemo {

    // 待计算的数字数组
    private static int[] nums = new int[40000];

    static {
        for (int i = 0; i < 40000; i++) {
            nums[i] = i + 1;
        }
    }

    // 计算线程数量
    private static final int THREAD_COUNT = 4;

    // CountDownLatch对象
    private static CountDownLatch threadLatch = new CountDownLatch(THREAD_COUNT);

    // 每个线程要计算的数字数量
    private static final int NUM_PER_THREAD = nums.length / THREAD_COUNT;

    // 线程池
    private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);

    // 等待计算线程全部完成的CountDownLatch对象
    private static CountDownLatch mainLatch = new CountDownLatch(THREAD_COUNT);

    // 计算结果
    private static volatile int result = 0;

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();

        // 启动计算线程
        for (int i = 0; i < THREAD_COUNT; i++) {
            int startIndex = i * NUM_PER_THREAD;
            int endIndex = (i == THREAD_COUNT - 1) ? nums.length : (i + 1) * NUM_PER_THREAD;
            executorService.execute(new Calculator(startIndex, endIndex, threadLatch, mainLatch));
        }

        // 等待计算线程全部完成
        mainLatch.await();

        System.out.println("计算结果是:" + result);
        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");

        // 关闭线程池
        executorService.shutdown();
    }

    /**
     * 计算线程
     */
    private static class Calculator implements Runnable {

        private int startIndex;

        private int endIndex;

        private CountDownLatch threadLatch;

        private CountDownLatch mainLatch;

        public Calculator(int startIndex, int endIndex, CountDownLatch threadLatch, CountDownLatch mainLatch) {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.threadLatch = threadLatch;
            this.mainLatch = mainLatch;
        }

        @Override
        public void run() {
            // 计算本线程负责的数字的和
            int sum = 0;
            for (int i = startIndex; i < endIndex; i++) {
                sum += nums[i];
            }
            // 将本线程计算的结果加入到总结果中
            synchronized (CountDownLatchDemo.class) {
                result += sum;
            }
            // 本线程计算完成后,将CountDownLatch的count减1
            threadLatch.countDown();

            try {
                // 等待其他计算线程完成
                threadLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 当所有计算线程都完成后,减少mainLatch的count
            mainLatch.countDown();
        }
    }
}

代码的逻辑很清晰,主线程先启动计算线程,然后等待所有计算线程完成后,将计算结果求和并输出。

示例二

假设我们有一个需求,要求一个线程等待多个线程全部执行完成后再执行。我们可以使用CountDownLatch来实现这个需求。

我们的实现代码如下:

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

public class CountDownLatchDemo {

    // CountDownLatch对象
    private static CountDownLatch threadLatch = new CountDownLatch(3);

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();

        // 启动3个线程
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        executorService.execute(new MyThread("线程1", threadLatch));
        executorService.execute(new MyThread("线程2", threadLatch));
        executorService.execute(new MyThread("线程3", threadLatch));
        executorService.shutdown();

        // 等待3个线程全部完成
        threadLatch.await();

        // 所有线程执行完成后输出
        System.out.println("所有线程执行完成");

        System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
    }

    /**
     * 自定义线程
     */
    private static class MyThread implements Runnable {

        private String name;

        private CountDownLatch threadLatch;

        public MyThread(String name, CountDownLatch threadLatch) {
            this.name = name;
            this.threadLatch = threadLatch;
        }

        @Override
        public void run() {
            try {
                // 模拟线程执行时间
                Thread.sleep((long) (Math.random() * 1000));
                System.out.println(name + " 执行完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 线程执行完成后,将CountDownLatch的count减1
            threadLatch.countDown();
        }
    }
}

代码的逻辑也很清晰,主线程先启动3个线程后,等待这3个线程全部完成后,输出一句话表示所有线程执行完成。

总结

以上就是CountDownLatch的详细讲解和应用示例,通过这些例子和说明,我们可以看出使用CountDownLatch可以方便地实现一些多线程协作的任务和需求,比如等待多个线程完成后再执行后续操作、分布式任务的执行等。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Java线程同步器CountDownLatch - Python技术站

(0)
上一篇 2023年5月18日
下一篇 2023年5月18日

相关文章

  • 详解spring与shiro集成

    对于“详解spring与shiro集成”的完整攻略,我可以提供以下步骤和代码示例供参考: 1. 添加shiro依赖 在项目的pom文件中,添加shiro的依赖: <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</…

    Java 2023年5月20日
    00
  • 博德之门2:加强版怎么修改存档 具体方法步骤详解

    下面是博德之门2:加强版怎么修改存档的具体方法步骤详解。 步骤一:备份存档 在进行存档修改操作之前,请先备份您的存档。这可以帮助您在修改出现问题时恢复到之前的存档状态。 步骤二:下载存档编辑器 下载名为“GIBBED.DIVINITY2.SAVEEDITO”的存档编辑器,该编辑器可以供玩家修改存档。您可以通过搜索引擎搜索并下载该编辑器。 步骤三:打开存档编辑…

    Java 2023年6月16日
    00
  • ssh框架实现文件上传下载实例代码

    使用SSH(Secure Shell)协议进行文件上传和下载是一种安全且可靠的方式。在此,我将为大家详细讲解如何使用SSH框架实现文件上传和下载,并提供两个示例代码供参考。 1. SSH框架实现文件上传 1.1 准备工作 在开始编写SSH框架实现文件上传之前,我们需要进行以下准备工作: 添加SSH框架的依赖: xml <dependency> &…

    Java 2023年5月20日
    00
  • jQuery+jsp实现省市县三级联动效果(附源码)

    实现省市县三级联动效果是Web开发中经常需要的功能之一。在这个过程中,jQuery 和 jsp 无疑是非常好的组合,因为 jQuery 可以方便的获取和操作DOM元素,jsp则具有动态生成html页面的优势。本文将分享一篇详细的攻略,教你如何使用 jQuery 和 jsp 实现省市县三级联动效果,并附上完整的源码。 一、前置知识 在阅读本篇攻略前,你需要具备…

    Java 2023年6月15日
    00
  • maven基础教程——简单了解maven的特点与功能

    Maven基础教程 —— 简单了解Maven的特点与功能 什么是Maven? Maven是一个强大的项目管理工具,主要用于构建、发布和管理Java项目。通过声明项目的依赖关系,Maven可以自动下载所需的库文件,并构建项目的目录结构。使用Maven可以实现一次性完成项目的编译、测试、打包和部署等工作。 Maven的特点 自动构建:Maven通过声明式的方式管…

    Java 2023年5月19日
    00
  • kafka手动调整分区副本数的操作步骤

    当需要手动调整Kafka集群中的某个主题的分区副本数时,可以通过添加或删除分区副本来实现。下面是手动调整分区副本数的操作步骤: 打开Kafka集群管理界面,例如Kafka Manager或Apache Kafka Web Console。 选择需要调整分区副本数的主题,点击进入主题管理页面。 打开分区列表,选择需要调整分区副本数的分区(例如第3个分区)。 点…

    Java 2023年5月20日
    00
  • springboot整合shardingjdbc实现分库分表最简单demo

    下面是一份完整的SpringBoot整合ShardingJDBC实现分库分表最简单demo的攻略: 一、前置条件 掌握SpringBoot和Maven的基础及配置方式; 了解什么是ShardingJDBC以及其分库分表的实现原理; 准备好使用的数据库及其账号密码。 二、添加依赖 在Maven的pom.xml文件中添加以下依赖: <dependency&…

    Java 2023年5月20日
    00
  • 深入理解TextView实现Rich Text–在同一个TextView设置不同字体风格

    深入理解TextView实现Rich Text的攻略如下: 1. 了解Spannable接口 TextView实现富文本的关键在于使用Spannable接口。Spannable是一个接口,用于控制文本的呈现方式,可以在TextView中实现不同的文本样式。 Spannable接口提供了许多实现富文本的方法,如ForegroundColorSpan、Backg…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部