详解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日

相关文章

  • 从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate

    下面我将详细讲解“从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate”的完整攻略。 前置要求 在正式进行搭建之前,需要确保你已经安装配置好以下软件: JDK Maven Tomcat IDE(推荐使用IntelliJ IDEA) 步骤一:创建Maven项目 首先,我们需要创建一个Maven项目。在IDE中,找到创建M…

    Java 2023年5月20日
    00
  • SpringBoot接口返回结果封装方法实例详解

    SpringBoot接口返回结果封装方法实例详解 在SpringBoot中,我们可以使用接口返回结果封装方法来统一处理接口返回结果。本文将详细讲解SpringBoot接口返回结果封装方法实例的完整攻略,并提供两个示例。 1. 接口返回结果封装方法 在SpringBoot中,我们可以使用接口返回结果封装方法来统一处理接口返回结果。以下是接口返回结果封装方法的基…

    Java 2023年5月15日
    00
  • Java实现批量导出导入数据及附件文件zip包

    下面我来详细讲解一下“Java实现批量导出导入数据及附件文件zip包”的完整攻略。 一、准备工作 1.引入相关依赖 我们需要引入以下依赖: <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-compress</ar…

    Java 2023年5月19日
    00
  • 详解Servlet3.0新特性(从注解配置到websocket编程)

    详解Servlet3.0新特性(从注解配置到websocket编程) 1. 前言 Servlet3.0是JavaEE6中一个主要的更新版本,它引入了很多新的特性与API,其中最值得我们关注的是注解配置和Websocket编程。 本文将详细展示Servlet3.0中的这些新特性,并通过具体的示例来帮助读者更好地理解这些特性的使用方法。 2. 注解配置 在Ser…

    Java 2023年6月15日
    00
  • 详解基于Spring Data的领域事件发布

    以下是《详解基于Spring Data的领域事件发布》的完整攻略: 1. 概述 领域事件 领域事件是指在领域中发生的一些重要操作或数据变化,如订单创建、库存减少等。它们可以触发其他业务逻辑,也可以被其他业务逻辑订阅并处理。 Spring Data Spring Data 是 Spring 社区为简化数据库访问和实现数据持久化的开源框架。它提供了丰富的 API…

    Java 2023年5月20日
    00
  • java设计模式之实现对象池模式示例分享

    Java 设计模式之实现对象池模式示例分享 什么是对象池模式 对象池模式是一种创建对象的基本模式,它的主要思想是在对象池中预先创建一定数量的对象,当需要使用对象时,从对象池中获取一个已经存在的对象并对其进行操作,而不是频繁创建新的对象。当对象使用完毕后,不是将其销毁,而是将其放回到对象池中,等待下一次被使用。 对象池模式的主要作用是降低应用程序创建和销毁对象…

    Java 2023年5月26日
    00
  • 初学者易上手的SSH-struts2 01环境搭建(图文教程)

    我来详细讲解一下 “初学者易上手的SSH-struts2 01环境搭建(图文教程)” 的完整攻略: 环境说明 本文的环境搭建基于以下环境版本: Java version: 1.8.0_221 Tomcat version: 9.0.22 Struts2 version: 2.5.22 MySQL version: 5.7.27 步骤1:安装Java 1.1 …

    Java 2023年5月20日
    00
  • maven仓库repositories和mirrors的配置及区别详解

    介绍 在使用Maven进行依赖管理时,常常会遇到一些有关仓库repositories和镜像mirrors的问题。本文将详细介绍这两个概念及其配置方式和区别。 仓库Repositories 仓库repositories是存储Maven构建的依赖和插件的位置。在Maven中有两种仓库:本地仓库和远程仓库。 本地仓库 指存储在本地计算机上的仓库,一般位于用户的.h…

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