Java中的Semaphore和CountDownLatch这两个工具类的使用方法和实际应用场景

简介: Java中的Semaphore和CountDownLatch这两个工具类的使用方法和实际应用场景

在现代的多线程编程中,Semaphore和CountDownLatch是两个非常常见和重要的工具类,它们都可以用来实现多线程间的同步和互斥,提高程序的并发性能和效率。本文将详细介绍Java中的Semaphore和CountDownLatch这两个工具类的使用方法和实际应用场景。

一、Semaphore

1.1 概述

Semaphore是Java中的一个同步工具类,用来控制多个线程对共享资源的访问。它主要用于控制并发线程的数量,可以设置一定数量的许可证,每当一个线程访问共享资源时,会消耗一个许可证,当许可证用尽时,其他线程就会被阻塞,直到有线程释放许可证为止。

1.2 使用

Semaphore的常用方法如下:

  • acquire(int permits): 获取指定数量的许可证,如果没有足够的许可证,当前线程将会被阻塞。
  • tryAcquire(int permits): 尝试获取指定数量的许可证,如果可以获取则返回true,否则返回false。
  • release(int permits): 释放指定数量的许可证,唤醒被阻塞的线程。

使用Semaphore的典型场景是控制线程的并发数量,下面我们来看一个简单的例子:

import java.util.concurrent.Semaphore;

public class SemaphoreTest {
   
    public static void main(String[] args) {
   
        int N = 8; // 任务数
        Semaphore semaphore = new Semaphore(5); // 最大并发数为5

        for (int i = 0; i < N; i++) {
   
            new Worker(i, semaphore).start(); // 启动多个线程
        }
    }

    static class Worker extends Thread {
   
        private int num;
        private Semaphore semaphore;

        public Worker(int num, Semaphore semaphore) {
   
            this.num = num;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
   
            try {
   
                semaphore.acquire(); // 获取许可证
                System.out.println("Worker " + num + " is working");
                Thread.sleep(2000);
                System.out.println("Worker " + num + " has finished");
                semaphore.release(); // 释放许可证
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
        }
    }
}

上面的代码中,我们创建了一个Semaphore对象,并设置最大并发数为5。然后创建了8个线程,每当一个线程开始工作时,需要先获取一个许可证,如果没有足够的许可证,则会被阻塞,直到其他线程释放许可证为止。这样就可以有效控制线程的并发数量,避免系统资源过度消耗。

二、CountDownLatch

2.1 概述

CountDownLatch也是Java中的一个同步工具类,它用于控制一个或多个线程等待其他线程完成任务后再执行。CountDownLatch的工作方式比较简单,它会在初始化时设置一个计数器,每当一个任务完成时,就将计数器减一;当计数器为0时,代表所有任务都已经完成,等待的线程可以开始执行了。

2.2 使用

CountDownLatch的常用方法如下:

  • CountDownLatch(int count):初始化一个CountDownLatch对象,并设置计数器初始值。
  • void await():调用此方法会使当前线程等待,直到计数器为0才继续执行。
  • void countDown():将计数器减1,当计数器为0时,等待的线程会被唤醒。

下面我们来看一个实际应用场景,假设要求在多个线程中进行文件拆分和合并操作,需要先将文件拆分成若干个块,然后多个线程并行处理这些块,最后再将处理结果合并成一个完整的文件。这个需求可以使用CountDownLatch来实现。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
   
    private static final int THREAD_COUNT = 4; // 线程数
    private static final CountDownLatch startLatch = new CountDownLatch(1); // 开始信号
    private static final CountDownLatch endLatch = new CountDownLatch(THREAD_COUNT); // 结束信号

    public static void main(String[] args) {
   
        for (int i = 0; i < THREAD_COUNT; i++) {
   
            new Thread(() -> {
   
                try {
   
                    System.out.println(Thread.currentThread().getName() + " is ready");
                    startLatch.await(); // 等待开始信号
                    System.out.println(Thread.currentThread().getName() + " is working");
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " has finished");
                    endLatch.countDown(); // 发送结束信号
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            }).start();
        }

        System.out.println("All threads are ready, start working...");
        startLatch.countDown(); // 发送开始信号
        try {
   
            endLatch.await(); // 等待所有线程结束
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
        System.out.println("All threads have finished their work");
    }
}

上面的代码中,我们创建了4个线程,并使用CountDownLatch来控制线程的同步和互斥。首先创建了两个CountDownLatch对象,一个用于发出开始信号,一个用于接收结束信号。然后启动了4个线程,每当一个线程开始工作时,需要等待开始信号发出,如果没有收到开始信号,则会一直等待;当所有线程都已经完成任务后,需要发送结束信号,以便主线程可以继续执行。

三、总结

通过以上示例,我们可以看到在实际应用中,Semaphore和CountDownLatch也都是非常实用的工具类,它们可以帮助程序员有效控制多线程的并发数量和任务执行顺序,提高程序的性能和效率。有了这两个工具类的帮助,我们可以更加方便地进行多线程编程,实现更加复杂的业务逻辑。需要注意的是,在使用这两个工具类时,应该结合实际需求场景来选择合适的方法和参数,避免程序出现不必要的死锁和阻塞。

目录
相关文章
|
11天前
|
Java 数据库连接 数据库
Java服务提供接口(SPI)的设计与应用剖析
Java SPI提供了一种优雅的服务扩展和动态加载机制,使得Java应用程序可以轻松地扩展功能和替换组件。通过合理的设计与应用,SPI可以大大增强Java应用的灵活性和可扩展性。
44 18
|
10天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
7天前
|
SQL JavaScript 前端开发
用Java来开发Hive应用
用Java来开发Hive应用
20 7
|
7天前
|
SQL JavaScript 前端开发
用Java、Python来开发Hive应用
用Java、Python来开发Hive应用
18 6
|
6天前
|
Java 数据库连接 开发者
Java中的异常处理机制:理解与应用
在Java编程中,异常处理是一个核心概念,它允许程序在遇到错误时优雅地恢复或终止。本文将深入探讨Java的异常处理机制,包括异常的分类、如何正确使用try-catch-finally块以及throw关键字。我们将通过实例来说明如何在Java应用程序中有效地捕获和处理异常,以确保程序的健壮性和稳定性。
|
6天前
|
Java 调度 开发者
Java中的多线程基础及其应用
【9月更文挑战第13天】本文将深入探讨Java中的多线程概念,从基本理论到实际应用,带你一步步了解如何有效使用多线程来提升程序的性能。我们将通过实际代码示例,展示如何在Java中创建和管理线程,以及如何利用线程池优化资源管理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧,帮助你更好地理解和应用多线程编程。
|
7天前
|
存储 负载均衡 Java
Jetty技术深度解析及其在Java中的实战应用
【9月更文挑战第3天】Jetty,作为一款开源的、轻量级、高性能的Java Web服务器和Servlet容器,自1995年问世以来,凭借其卓越的性能、灵活的配置和丰富的扩展功能,在Java Web应用开发中占据了举足轻重的地位。本文将详细介绍Jetty的背景、核心功能点以及在Java中的实战应用,帮助开发者更好地理解和利用Jetty构建高效、可靠的Web服务。
22 2
|
11天前
|
Java 数据处理
技术分享:高效与灵活并存——Java版通用树形结构转换工具的实现与应用
在软件开发中,树形结构的数据表现形式无处不在,从文件系统的目录树到组织架构的部门树,再到各类产品的分类结构。处理这些具有层级关系的数据时,将其转换为树形结构以便展示和操作显得尤为重要。Java作为一门成熟的编程语言,虽然提供了强大的集合框架,但并未直接提供树形结构转换的内置工具。因此,开发一个高效且灵活的通用树形结构转换工具成为许多项目中的必备需求。
21 2
|
1天前
|
Kubernetes Cloud Native Java
探索未来编程新纪元:Quarkus带你秒建高性能Kubernetes原生Java应用,云原生时代的技术狂欢!
Quarkus 是专为 Kubernetes 设计的全栈云原生 Java 框架,凭借其轻量级、快速启动及高效执行特性,在 Java 社区脱颖而出。通过编译时优化与原生镜像支持,Quarkus 提升了应用性能,同时保持了 Java 的熟悉度与灵活性。本文将指导你从创建项目、编写 REST 控制器到构建与部署 Kubernetes 原生镜像的全过程,让你快速上手 Quarkus,体验高效开发与部署的乐趣。
8 0
|
1天前
|
Java 开发者
Java中的异常处理机制:理解与应用
在Java编程中,异常处理是确保程序稳定性和可靠性的关键。本文将深入探讨Java的异常处理机制,包括异常的分类、捕获和处理方法,以及如何有效地使用这些工具来提高代码质量。