java并发编程JUC CountDownLatch线程同步

CountDownLatch 是一个线程同步工具,用于让特定的线程等待其他线程完成操作后再继续执行。当某个线程需要等待,直到一个或多个其他线程完成操作后,它们才能继续执行时,就可以使用 CountDownLatch。

1. CountDownLatch 的基本使用

1.1 原理和基本用法

CountDownLatch 的原理是,一个线程等待其他线程完成某些操作之后再执行。初始化 CountDownLatch 时需要指定“计数器”的初始值,即需要等待的线程数量。当有线程完成一个操作时,计数器的值减一。当计数器的值为0时,等待线程就可以继续执行。具体代码如下所示:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        int count = 5;
        CountDownLatch countDownLatch = new CountDownLatch(count);
        for (int i = 0; i < count; i++) {
            Thread thread = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行完成");
                // 计数器减一
                countDownLatch.countDown();
            });
            thread.start();
        }
        System.out.println("等待5个线程执行完成...");
        // 主线程等待计数器归零
        countDownLatch.await();
        System.out.println("5个线程执行完成,主线程继续执行。");
    }
}

以上代码中,我们创建了一个 CountDownLatch 对象,初始计数器的值为5。然后创建5个线程,每个线程执行完成后调用 countDownLatch.countDown() 方法,将计数器减一。主线程调用 countDownLatch.await() 方法等待计数器的值归零。当计数器的值为0时,主线程会继续执行。

1.2 示例说明

我们来看一个实际的例子,假设有一个任务需要将一个文件读入内存、进行处理、然后存储到数据库。我们使用三个线程完成这个任务,一个线程读取文件,一个线程进行处理,一个线程将处理结果存储到数据库。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.concurrent.CountDownLatch;

public class Task {
    private CountDownLatch countDownLatch = new CountDownLatch(2);
    private File file;

    public Task(File file) {
        this.file = file;
    }

    public void start() throws InterruptedException {
        Thread readThread = new Thread(() -> {
            try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    // do something
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        });
        readThread.start();
        Thread processThread = new Thread(() -> {
            // do something
            countDownLatch.countDown();
        });
        processThread.start();
        countDownLatch.await();
        Thread storeThread = new Thread(() -> {
            // do something
        });
        storeThread.start();
    }
}

在以上示例中,我们首先创建了一个 countDownLatch 对象(初始值为2),然后创建了两个线程,一个线程读取文件,一个线程进行处理。在这两个线程中,都需要等读取和处理完成后才能继续执行。使用 countDownLatch.countDown() 方法将计数器减一。主线程调用 countDownLatch.await() 等待计数器的值归零,然后再创建一个线程用于将处理结果存储到数据库。

2. CountDownLatch 的进阶使用

2.1 CountDownLatch 和线程池的结合使用

当我们使用线程池时,可能需要等待一些线程执行完毕后再执行一些操作。我们可以通过 CountDownLatch 来实现。

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

public class ThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
        int count = 5;
        CountDownLatch countDownLatch = new CountDownLatch(count);
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < count; i++) {
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行完成");
                // 计数器减一
                countDownLatch.countDown();
            });
        }
        System.out.println("等待5个线程执行完成...");
        // 主线程等待计数器归零
        countDownLatch.await();
        System.out.println("5个线程执行完成,主线程继续执行。");
        executorService.shutdown();
    }
}

以上代码中,我们使用了线程池来执行任务,但是我们还需要等待所有线程执行完成。在每个线程执行完成之后,我们调用了 countDownLatch.countDown() 方法,将计数器减一。主线程调用 countDownLatch.await() 方法等待计数器的值归零。当计数器的值为0时,主线程继续执行。

2.2 CountDownLatch 和 Semaphore 的结合使用

Semaphore 是另一种线程同步工具,它也是用于控制多个线程对共享资源的访问。CountDownLatch 和 Semaphore 有很多相似之处,例如都可以用来控制线程的执行顺序。下面我们来看一下 CountDownLatch 和 Semaphore 的结合使用。

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

public class SemaphoreDemo {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 5;
        Semaphore semaphore = new Semaphore(3);
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        for (int i = 0; i < threadCount; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 执行完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                    countDownLatch.countDown();
                }
            });
        }
        System.out.println("已启动 " + threadCount + " 个线程,准备执行任务...");
        //主线程阻塞等待子线程执行完毕
        countDownLatch.await();
        System.out.println("所有线程执行完毕");
        executorService.shutdown();
    }
}

在以上示例中,我们创建了一个 Semaphore(初始值为3),然后使用线程池启动了5个线程。每个线程执行时均会尝试获取 Semaphore 的许可(即执行 semaphore.acquire()),如果 Semaphore 的许可数量不足,则线程会阻塞等待,直到获取到许可为止。当每个线程执行完成后会释放 Semaphore 的许可(即执行 semaphore.release())。主线程使用 CountDownLatch 等待所有线程执行完成,然后关闭线程池并退出。

总结

CountDownLatch 是 Java 中常用的线程同步工具,可以实现主线程等待多个线程执行完成后再继续执行的功能。在实际的开发中,CountDownLatch 常常和其他线程同步工具一起使用,例如和 Semaphore 结合使用可以控制多个线程对共享资源的访问。我们需要根据具体的业务需求来灵活使用不同的线程同步工具,才能更好地提高程序的并发性和性能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java并发编程JUC CountDownLatch线程同步 - Python技术站

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

相关文章

  • 拳皇(Java简单的小程序)代码实例

    拳皇(Java简单的小程序)是一个基于Java Swing的小游戏应用程序,主要通过键盘控制实现不同的角色之间的战斗和移动。下面是该小程序实现的完整攻略,包含基本的代码结构、功能实现和示例说明。 代码结构 拳皇小程序的代码结构主要包括以下几个部分: Main.java:程序入口,包含主函数和窗口初始化等功能。 GamePanel.java:游戏主面板,包含游…

    Java 2023年5月23日
    00
  • 详解APP微信支付(java后台_统一下单和回调)

    详解APP微信支付(java后台_统一下单和回调) 一、前言 在移动APP中,使用微信支付功能是非常常见的需求,而且使用微信支付也是比较方便和快捷的。本文将详细介绍如何在Java后台中实现微信支付的功能。主要包括两部分:统一下单和回调。本文介绍的支付接口都是官方的API接口,并采用了最新的V3版本。 二、统一下单 下单接口是微信支付功能的核心,接口名称为:h…

    Java 2023年5月27日
    00
  • 解决java maven项目找不到jconsole-1.8.0.jar和tools-1.8.0.jar包问题

    解决java maven项目找不到jconsole-1.8.0.jar和tools-1.8.0.jar包问题的完整攻略如下: 问题说明 当使用Maven构建Java项目时,有时候会出现找不到jconsole-1.8.0.jar和tools-1.8.0.jar包的问题。这是因为Java从JDK 9开始,已经将jconsole.jar、tools.jar等jar…

    Java 2023年5月20日
    00
  • Java中Spock框架Mock对象的方法经验总结

    Java中Spock框架Mock对象的方法经验总结 简介 Spock是一个基于Geb和JUnit的开源Java测试框架,它支持BDD(行为驱动开发)并提供了很多有用的功能。其中一个最常用的功能是Mock对象。这篇攻略将介绍如何在Java中使用Spock框架Mock对象。 Mock对象的定义 Mock对象是经过模拟的对象,代替了真实的对象。Mock对象可以控制…

    Java 2023年5月26日
    00
  • Java实现实时监控目录下文件变化的方法

    Java实现实时监控目录下文件变化的方法可以通过使用Java 7或更高版本中的WatchService类来实现。下面是实现此方法的详细步骤。 第一步:创建WatchService对象 使用Java的标准库提供的WatchService类,可以监控文件系统中的更改。可以通过以下方式创建WatchService对象: WatchService watchServ…

    Java 2023年5月20日
    00
  • SpringMVC form标签引入及使用方法

    下面是关于“SpringMVC form标签引入及使用方法”的完整攻略。 引入form标签库 首先,需要在JSP页面中引入 SpringMVC 的 form 标签库,代码如下: <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form&qu…

    Java 2023年6月15日
    00
  • 浅谈idea live template高级知识_进阶(给方法,类,js方法添加注释)

    浅谈idea live template高级知识_进阶(给方法,类,js方法添加注释) IDEA中的Live Templates是一个非常方便的功能,可以帮助我们快速地插入常用的代码格式。本文将介绍如何使用Live Templates为方法、类和JS方法添加注释。 为方法添加注释 步骤1:打开Live Templates设置 首先,要打开IDEA的Live …

    Java 2023年6月15日
    00
  • Spring中事务管理的四种方法(银行转账为例)

    请看我以下的详细讲解。 Spring中事务管理的四种方法 Spring中提供了四种常用的方式来管理事务,分别是: 通过AOP实现声明式事务管理 通过编程式事务管理 通过注解实现声明式事务管理 通过TransactionTemplate实现编程式事务管理 对于每种事务管理方式,我们将通过银行转账的例子进行说明。 1. 通过AOP实现声明式事务管理 在这种方式中…

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