轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

    • 核心

    • 并发

      • Java并发-线程基础与synchronized关键字
      • Java并发-重入锁ReentrantLock详解与实践
      • Java并发-信号量Semaphore
        • 一、基础用法
        • 二、核心方法详解
          • 1、获取许可证
          • 2、释放许可证
          • 3、状态查询
        • 三、实际应用场景
          • 1、限流控制
          • 2、连接池管理
        • 四、高级特性
          • 1、公平性
          • 2、的灵活用法
        • 五、使用注意事项
          • 1、异常安全
          • 2、避免许可证泄漏
          • 3、中断处理
        • 六、与其他并发工具的对比
        • 七、小结
      • Java并发-读写锁ReadWriteLock
      • Java并发-倒计时器CountDownLatch
      • Java并发-栅栏CyclicBarrier
      • Java并发-LockSupport线程阻塞工具类
      • Java并发-线程池ThreadPoolExecutor
      • Java并发-阻塞队列BlockingQueue
      • Java并发-以空间换时间之ThreadLocal
      • Java并发-无锁策略CAS与atomic包
      • Java并发-JDK并发容器
      • Java并发-异步调用结果之Future和CompletableFuture
      • Java并发-Fork Join框架
      • Java并发-调试与诊断
    • 经验

    • JVM

    • 企业应用

  • Spring

  • 其他语言

  • 工具

  • 后端
  • Java
  • 并发
轩辕李
2018-06-06
目录

Java并发-信号量Semaphore

在高并发的场景下,我们经常需要控制同时访问某个资源的线程数量。比如说,一个数据库连接池只能支持20个并发连接,或者一个第三方API每秒只能处理100个请求。这时候就需要用到信号量(Semaphore)这个强大的并发控制工具。

想象一下这样的场景:你开了一家火锅店,店里只有10张桌子。顾客来了要先拿号排队,有桌子空出来才能进去吃饭,吃完后要把桌子让出来给下一位客人。Semaphore 就像是这个排队系统,控制着同时能够使用资源的线程数量。

# 一、基础用法

先来看一个简单的例子:

import java.util.concurrent.*;

public class SemaphoreDemo {
    // 创建一个许可证数量为5的信号量
    private final Semaphore semaphore = new Semaphore(5);
    
    public void doWork(int workerId) {
        try {
            // 获取许可证
            semaphore.acquire();
            System.out.println("工作者 " + workerId + " 开始工作,当前可用许可证:" + semaphore.availablePermits());
            
            // 模拟工作时间
            Thread.sleep(2000);
            
            System.out.println("工作者 " + workerId + " 工作完成");
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 释放许可证
            semaphore.release();
            System.out.println("工作者 " + workerId + " 释放许可证");
        }
    }
    
    public static void main(String[] args) {
        SemaphoreDemo demo = new SemaphoreDemo();
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        // 启动10个任务,但同时只能有5个执行
        for (int i = 1; i <= 10; i++) {
            final int workerId = i;
            executor.submit(() -> demo.doWork(workerId));
        }
        
        executor.shutdown();
    }
}

运行这个程序,你会发现虽然提交了10个任务,但同时执行的永远不会超过5个。

# 二、核心方法详解

Semaphore 提供了多个获取和释放许可证的方法:

# 1、获取许可证

  • acquire() - 获取一个许可证,如果没有可用许可证则阻塞等待
  • acquire(int permits) - 获取指定数量的许可证
  • acquireUninterruptibly() - 获取许可证,不响应中断
  • tryAcquire() - 尝试获取许可证,立即返回结果,不等待
  • tryAcquire(long timeout, TimeUnit unit) - 在指定时间内尝试获取许可证

# 2、释放许可证

  • release() - 释放一个许可证
  • release(int permits) - 释放指定数量的许可证

# 3、状态查询

  • availablePermits() - 返回当前可用的许可证数量
  • getQueueLength() - 返回正在等待获取许可证的线程数量
  • hasQueuedThreads() - 是否有线程在等待

# 三、实际应用场景

# 1、限流控制

public class ApiRateLimiter {
    // 每秒最多处理100个请求
    private final Semaphore semaphore = new Semaphore(100);
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
    public ApiRateLimiter() {
        // 每秒恢复所有许可证
        scheduler.scheduleAtFixedRate(() -> {
            int used = 100 - semaphore.availablePermits();
            if (used > 0) {
                semaphore.release(used);
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
    
    public boolean tryProcessRequest() {
        return semaphore.tryAcquire();
    }
}

# 2、连接池管理

public class DatabaseConnectionPool {
    private final Semaphore connectionSemaphore;
    private final Queue<Connection> availableConnections;
    
    public DatabaseConnectionPool(int maxConnections) {
        this.connectionSemaphore = new Semaphore(maxConnections);
        this.availableConnections = new ConcurrentLinkedQueue<>();
        
        // 初始化连接
        for (int i = 0; i < maxConnections; i++) {
            availableConnections.offer(createConnection());
        }
    }
    
    public Connection getConnection() throws InterruptedException {
        connectionSemaphore.acquire();
        return availableConnections.poll();
    }
    
    public void returnConnection(Connection conn) {
        availableConnections.offer(conn);
        connectionSemaphore.release();
    }
    
    private Connection createConnection() {
        // 创建数据库连接的逻辑
        return null;
    }
}

# 四、高级特性

# 1、公平性

Semaphore 支持公平模式,确保等待时间最长的线程优先获得许可证:

// 公平模式的信号量
Semaphore fairSemaphore = new Semaphore(5, true);

# 2、的灵活用法

public class FlexibleResourceAccess {
    private final Semaphore semaphore = new Semaphore(3);
    
    public void quickTask() {
        // 立即尝试获取,不等待
        if (semaphore.tryAcquire()) {
            try {
                System.out.println("执行快速任务");
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                semaphore.release();
            }
        } else {
            System.out.println("资源繁忙,跳过任务");
        }
    }
    
    public void timeoutTask() {
        try {
            // 最多等待5秒
            if (semaphore.tryAcquire(5, TimeUnit.SECONDS)) {
                try {
                    System.out.println("执行超时任务");
                    Thread.sleep(2000);
                } finally {
                    semaphore.release();
                }
            } else {
                System.out.println("等待超时,放弃任务");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

# 五、使用注意事项

# 1、异常安全

永远在 finally 块中释放许可证,确保即使发生异常也能正确释放:

semaphore.acquire();
try {
    // 业务逻辑
} finally {
    semaphore.release(); // 确保释放
}

# 2、避免许可证泄漏

每次 acquire() 都要对应一个 release(),否则会导致许可证永久丢失。

# 3、中断处理

acquire() 方法会响应中断,要正确处理 InterruptedException:

try {
    semaphore.acquire();
    // 业务逻辑
} catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // 恢复中断状态
    return;
} finally {
    semaphore.release();
}

# 六、与其他并发工具的对比

  • CountDownLatch:一次性使用,等待多个线程完成
  • CyclicBarrier:可重复使用,等待多个线程到达同步点
  • Semaphore:控制同时访问资源的线程数量,可重复使用

Semaphore 更适合资源池管理和流量控制场景,而 CountDownLatch 和 CyclicBarrier 更适合线程间的协调同步。

# 七、小结

Semaphore 是 Java 并发编程中的重要工具,它通过许可证机制优雅地解决了资源访问控制问题。无论是限流、连接池管理,还是其他需要控制并发数的场景,Semaphore 都能提供简洁而高效的解决方案。

关键是要理解它的核心思想:通过许可证来控制同时访问共享资源的线程数量。掌握了这个思想,你就能在各种场景下灵活运用这个强大的并发工具了。

编辑 (opens new window)
#Semaphore
上次更新: 2025/08/14
Java并发-重入锁ReentrantLock详解与实践
Java并发-读写锁ReadWriteLock

← Java并发-重入锁ReentrantLock详解与实践 Java并发-读写锁ReadWriteLock→

最近更新
01
AI时代的编程心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code实战之供应商切换工具
08-18
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式