Java中的Semaphore(信号量)全面解析

 更新时间:2025年08月20日 09:28:58   作者:MOONNIFE  
信号量(Semaphore)是Java多线程兵法中的一种JDK内置同步器,通过它可以实现多线程对公共资源的并发访问控制,这篇文章主要介绍了Java中Semaphore(信号量)的相关资料,需要的朋友可以参考下

前言

Semaphore 是 Java 并发包(java.util.concurrent)中的核心同步工具类,用于​​控制对共享资源的并发访问数量​​,通过“许可证”机制实现资源限流。下面从多个维度全面解析其设计原理和应用实践。

一、基本概念与核心原理​​

1. 核心模型​​

  • ​信号量模型​​:
    Semaphore 维护一个​​许可证计数器​​(permits),通过原子操作控制资源访问:
    • ​​acquire()​​(P操作):申请许可证,计数器减 1;若无可用许可证,线程阻塞。
    • ​​release()​​(V操作):释放许可证,计数器加 1,唤醒阻塞线程。
  • ​公平性​​:
    • ​非公平模式(默认)​​:允许线程插队获取许可,吞吐量高但可能引发线程饥饿。
    • ​公平模式​​:按 FIFO 顺序分配许可,避免饥饿但性能较低。

​​2. 底层实现​​

基于 ​​AQS(AbstractQueuedSynchronizer)​​ 的共享锁模式实现:

  • ​状态变量​​:AQS 的 state 字段存储当前可用许可证数量。
  • ​关键源码逻辑​​:
    // 非公平模式尝试获取许可
    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 || compareAndSetState(available, remaining)) 
                return remaining; // 负数表示获取失败
        }
    }
    // 释放许可
    protected final boolean tryReleaseShared(int releases) {
        for (;;) {
            int current = getState();
            int next = current + releases;
            if (compareAndSetState(current, next)) 
                return true; // 唤醒阻塞线程
        }
    }

​二、使用方式与代码示例​​

1. 基础用法​​

import java.util.concurrent.Semaphore;

public class PrinterService {
    private final Semaphore semaphore = new Semaphore(3); // 初始化3个许可证

    public void printDocument(String docName) throws InterruptedException {
        semaphore.acquire(); // 获取许可(阻塞直到可用)
        try {
            System.out.println("Printing: " + docName);
            Thread.sleep(1000); // 模拟打印耗时
        } finally {
            semaphore.release(); // 必须释放许可!
        }
    }
}

​​2. 高级方法:超时与批量操作​​

// 尝试获取许可(超时控制)
if (semaphore.tryAcquire(2, 500, TimeUnit.MILLISECONDS)) {
    try {
        // 执行任务
    } finally {
        semaphore.release(2); // 释放多个许可
    }
} else {
    System.out.println("获取许可超时!");
}

3. 多线程场景示例​​

public static void main(String[] args) {
    PrinterService service = new PrinterService();
    for (int i = 0; i < 10; i++) {
        new Thread(() -> {
            try {
                service.printDocument("Doc-" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

​输出效果​​:

Printing: Doc-Thread-0  
Printing: Doc-Thread-1  
Printing: Doc-Thread-2  // 仅3个线程并发执行  
Thread-2 释放许可 → Thread-3 开始打印  
...  

三、优缺点分析​​

​维度​​优点​​缺点​
​性能​读操作无锁,高并发场景吞吐量高(尤其非公平模式)。写操作需复制数组,频繁修改时性能差(时间复杂度 O(n))。
​资源控制​精准限制并发量,避免资源过载。许可证泄漏(未释放)会导致资源逐渐耗尽。
​一致性​弱一致性模型,迭代器安全(基于快照)。无法实时感知最新修改,迭代期间数据可能过期。
​灵活性​支持动态调整许可证数量、超时控制、批量操作。复杂同步需求需结合其他工具(如 CountDownLatch)。

四、适用场景​​

1. 资源池管理​​

  • ​数据库连接池​​:限制同时使用的连接数,防止连接耗尽。
    public class ConnectionPool {
        private final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS);
        private final BlockingQueue<Connection> pool = new LinkedBlockingQueue<>();
    
        public Connection borrow() throws InterruptedException {
            semaphore.acquire();
            return pool.take();
        }
        public void release(Connection conn) {
            pool.offer(conn);
            semaphore.release();
        }
    }

2. 流量控制​​

  • ​API 限流​​:限制每秒请求数,防止服务雪崩。
    public class RateLimiter {
        private final Semaphore semaphore = new Semaphore(100); // 每秒100个请求
        private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
        public RateLimiter() {
            scheduler.scheduleAtFixedRate(() -> 
                semaphore.release(semaphore.drainPermits()), 1, 1, TimeUnit.SECONDS);
        }
        public void callAPI(Runnable task) throws InterruptedException {
            semaphore.acquire();
            task.run();
        }
    }

3. 物理资源共享​​

  • ​打印机/文件系统​​:控制多线程访问共享硬件资源。
    semaphore.acquire();
    try {
        Files.write(Paths.get("log.txt"), data, StandardOpenOption.APPEND);
    } finally {
        semaphore.release();
    }

​五、注意事项与最佳实践​​

  • ​避免许可证泄漏​​:
    • 确保 release() 在 finally 块中调用,防止异常导致许可未释放。
  • ​合理选择公平性​​:
    • 高吞吐场景用非公平模式,严格顺序需求用公平模式。
  • ​控制许可证数量​​:
    • 过多导致资源浪费,过少引发线程饥饿(通过监控 availablePermits() 动态调整)。
  • ​避免嵌套死锁​​:
    • 若需多信号量,按固定顺序获取(如先 A 后 B),防止交叉等待。

总结​​

Semaphore 是解决​​资源并发控制​​的利器,尤其适合​​读多写少、资源池限流、API控频​​等场景。其基于 AQS 的实现兼顾了灵活性与性能,但需注意许可证管理的原子性和释放的可靠性。在高并发系统中,合理使用 Semaphore 能有效提升系统稳定性与资源利用率🔥。

到此这篇关于Java中的Semaphore(信号量)的文章就介绍到这了,更多相关Java中Semaphore内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java使用iText生成PDF的步骤和示例

    Java使用iText生成PDF的步骤和示例

    iText 是一个用于创建和处理 PDF 文档的开源 Java 库,iText 主要用于生成 PDF 文件,可以将文本、图像、表格、列表等内容添加到 PDF 中,同时支持对 PDF 进行编辑、合并、分割、加密、数字签名等操作,本文介绍了Java使用iText生成PDF的步骤和示例
    2024-10-10
  • Spring实例化对象的几种常见方式

    Spring实例化对象的几种常见方式

    Spring框架作为一个轻量级的控制反转容器,为开发者提供了多种对象实例化的策略,通过这些策略,开发者可以更加灵活地控制对象的生命周期和依赖关系,无论是通过XML配置、注解配置还是Java配置,Spring都能轻松地实现对象的实例化,本文将介绍Spring实例化对象的几种常见方式
    2024-12-12
  • Java代码精简之道(推荐)

    Java代码精简之道(推荐)

    这篇文章主要给大家介绍了Java代码精简之道,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • java多线程编程之线程的生命周期

    java多线程编程之线程的生命周期

    线程要经历开始(等待)、运行、挂起和停止四种不同的状态。这四种状态都可以通过Thread类中的方法进行控制。下面给出了Thread类中和这四种状态相关的方法
    2014-01-01
  • Java避免UTF-8的csv文件打开中文出现乱码的方法

    Java避免UTF-8的csv文件打开中文出现乱码的方法

    这篇文章主要介绍了Java避免UTF-8的csv文件打开中文出现乱码的方法,结合实例形式分析了java操作csv文件时使用utf-16le编码与utf8编码相关操作技巧,需要的朋友可以参考下
    2019-07-07
  • 详解Java高阶语法Volatile

    详解Java高阶语法Volatile

    这篇文章主要介绍了Java高阶语法Volatile,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • java并发学习之BlockingQueue实现生产者消费者详解

    java并发学习之BlockingQueue实现生产者消费者详解

    这篇文章主要介绍了java并发学习之BlockingQueue实现生产者消费者详解,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Java使用Arrays.asList报UnsupportedOperationException的解决

    Java使用Arrays.asList报UnsupportedOperationException的解决

    这篇文章主要介绍了Java使用Arrays.asList报UnsupportedOperationException的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • JDBC三层架构深入刨析

    JDBC三层架构深入刨析

    三层架构是一种软件设计架构,是一种组织代码的手段和方法,三层架构的优点是扩展性好,复用性高;缺点是步骤多,比较繁琐;代码多,效率降低
    2022-12-12
  • java设计模式责任链模式原理案例详解

    java设计模式责任链模式原理案例详解

    一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现
    2021-09-09

最新评论