当前位置: 首页 > article >正文

JUC 三大辅助类: CountDownLatch CyclicBarrier Semaphore

在 Java 并发编程领域,java.util.concurrent(JUC)包提供了丰富的工具来帮助开发者处理多线程环境下的复杂问题。其中,CountDownLatchCyclicBarrierSemaphore这三大辅助类,在协调线程同步、控制并发访问等方面发挥着重要作用。本文将深入探讨这三个类的原理、使用场景以及具体的代码示例。

CountDownLatch

原理与功能

CountDownLatch是一个同步辅助类,它允许一个或多个线程等待,直到其他线程完成一组操作。其内部维护了一个计数器,通过countDown()方法递减计数,当计数器减为 0 时,所有等待的线程将被释放。

代码示例

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        int numThreads = 5;
        CountDownLatch latch = new CountDownLatch(numThreads);

        for (int i = 0; i < numThreads; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 开始工作");
                try {
                    Thread.sleep((long) (Math.random() * 1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 工作完成");
                latch.countDown();
            }).start();
        }

        try {
            System.out.println("主线程等待所有线程完成...");
            latch.await();
            System.out.println("所有线程已完成,主线程继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,主线程创建了一个CountDownLatch,初始计数为 5。每个子线程在完成工作后调用countDown()方法,主线程通过await()方法等待,直到所有子线程都调用了countDown(),即计数器为 0,然后主线程继续执行。

使用场景

  • 任务编排:在一个复杂的任务中,可能需要多个子任务并行执行,当所有子任务完成后,再进行下一步操作。例如,在一个数据处理系统中,多个线程分别处理不同部分的数据,当所有线程都处理完后,再进行数据汇总。
  • 测试场景:在编写多线程测试用例时,可以使用CountDownLatch来确保所有线程都到达某个状态后,再进行后续的断言和验证。

CyclicBarrier

原理与功能

CyclicBarrier也是一个同步辅助类,它允许一组线程相互等待,直到所有线程都到达某个屏障点。与CountDownLatch不同的是,CyclicBarrier可以重复使用。当所有线程都到达屏障点后,CyclicBarrier可以执行一个可选的Runnable任务,然后所有线程继续执行。

代码示例

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int numThreads = 3;
        CyclicBarrier barrier = new CyclicBarrier(numThreads, () -> {
            System.out.println("所有线程已到达屏障点,开始下一步操作");
        });

        for (int i = 0; i < numThreads; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 开始工作");
                try {
                    Thread.sleep((long) (Math.random() * 1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    System.out.println(Thread.currentThread().getName() + " 到达屏障点");
                    barrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 继续工作");
            }).start();
        }
    }
}

在这个示例中,创建了一个CyclicBarrier,当 3 个线程都调用await()方法到达屏障点时,会执行传入的Runnable任务(打印一条消息),然后所有线程继续执行后续操作。

使用场景

  • 多阶段任务:在一个任务中,可能存在多个阶段,每个阶段需要所有线程都完成上一阶段的工作后,才能继续进行。例如,在一个分布式计算任务中,每个节点完成局部计算后,需要等待所有节点都完成,然后再进行全局计算。
  • 并发控制:通过CyclicBarrier可以实现对并发线程的控制,确保在某个时间点,所有线程都处于同一状态,然后再继续执行。

Semaphore

原理与功能

Semaphore是一个计数信号量,它通过控制一个许可证(permit)的数量来控制对共享资源的并发访问。线程在访问共享资源前,需要先获取一个许可证,如果没有可用的许可证,线程将被阻塞,直到有许可证可用。当线程访问完共享资源后,需要释放许可证。

代码示例

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        int availablePermits = 3;
        Semaphore semaphore = new Semaphore(availablePermits);

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 尝试获取许可证");
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 获取到许可证,开始访问共享资源");
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println(Thread.currentThread().getName() + " 访问共享资源结束,释放许可证");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}

在上述代码中,创建了一个Semaphore,初始许可证数量为 3。5 个线程尝试获取许可证,最多只有 3 个线程可以同时获取到许可证并访问共享资源,其他线程将被阻塞,直到有许可证被释放。

使用场景

  • 资源控制:在数据库连接池、线程池等场景中,通过Semaphore可以控制对资源的并发访问数量,避免资源被过度使用。例如,在数据库连接池,Semaphore可以控制同时获取数据库连接的线程数量,防止数据库连接数过多导致性能下降。
  • 限流:在网络应用中,可以使用Semaphore实现限流功能,控制单位时间内访问某个接口的请求数量。

结语

感谢您的阅读!如果您对 JUC 包中的其他组件或并发编程话题有任何疑问或见解,欢迎继续探讨。


http://www.kler.cn/a/532240.html

相关文章:

  • 2. 【.NET Aspire 从入门到实战】--理论入门与环境搭建--.NET Aspire 概览
  • 华为云kubernetes部署deepseek r1、ollama和open-webui(已踩过坑)
  • STM32F103ZET6完整技术点(持续更新~)
  • Math数字类
  • 前端 | JavaScript中的reduce方法
  • Java创建对象有几种方式?
  • Chromium132 编译指南 - Android 篇(七):安装其他构建依赖项
  • 信息学奥赛一本通 2088:【22CSPJ普及组】逻辑表达式(expr) | 洛谷 P8815 [CSP-J 2022] 逻辑表达式
  • Java导出Excel简单工具类
  • 基于python去除知乎图片水印
  • Starrocks 对比 Clickhouse
  • 柔性数组与c/c++程序中内存区域的划分
  • 【办公类-99-01】20250201学具PDF打印会缩小一圈——解决办法:换一个PDF阅读器
  • TCP相关实验
  • 2025系统架构师---论数据访问层设计技术及其应用
  • 计算机网络——三种交换技术
  • 【Daily Code】leetcode热题100道
  • Day35-【13003】短文,什么是双端队列?栈和队列的互相模拟,以及解决队列模拟栈时出栈时间开销大的方法
  • Linux命令运行原理及权限管理
  • linux 进程补充
  • Acwing.基础课.排列数字(c++题解)
  • 前部分知识复习03
  • Java之类和对象
  • billd-live 一款开源、免费、技术先进的直播系统
  • ubuntu22.04(GUN)安装蓝牙驱动
  • 仿真设计|基于51单片机的光照、温湿度及PM2.5检测报警系统