目录
- 一、简介
- 二、API
- 三、使用
- 3.1 demo1
- 3.1 demo2
- 四、适用场景
一、简介
Semaphore
(信号量)是 Java 中用于控制同时访问特定资源的线程数量的工具类。Semaphore 维护了一组许可证,线程在访问资源之前必须先获取许可证,访问完毕后再释放许可证,从而限制同时访问资源的线程数量。
Semaphore 主要包括两个核心方法:
acquire()
: 当一个线程希望访问资源时,调用 acquire() 方法来获取一个许可证。如果当前没有可用的许可证,线程将被阻塞,直到有可用的许可证为止。
release()
: 当一个线程访问资源完毕后,调用 release() 方法来释放一个许可证,使其他等待许可证的线程可以继续执行。
除了上述方法外,Semaphore 还提供了一些其他方法来获取当前可用的许可证数量、设置初始许可证数量等。
二、API
- 非公平
public Semaphore(int permits);//permits就是允许同时运行的线程数目- 公平
public Semaphore(int permits,boolean fair);//permits就是允许同时运行的线程数目,fair=true代表公平- 创建一个信号量
Semaphore semaphore = new Semaphore(int permits);- 从信号量中获取一个许可
semaphore.acquire();- 释放一个许可(在释放许可之前,必须先获获得许可。)
semaphore.release();- 尝试获取一个许可,若获取成功返回true,若获取失败返回false
semaphore.tryAcquire();
- // 创建具有给定的许可数和非公平的公平设置的 Semaphore。
Semaphore(int permits)- // 创建具有给定的许可数和给定的公平设置的 Semaphore。
Semaphore(int permits, boolean fair)- // 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void acquire()- // 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。
void acquire(int permits)- // 从此信号量中获取许可,在有可用的许可前将其阻塞。
void acquireUninterruptibly()- // 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。
void acquireUninterruptibly(int permits)- // 返回此信号量中当前可用的许可数。
int availablePermits()- // 获取并返回立即可用的所有许可。
int drainPermits()- // 返回一个 collection,包含可能等待获取的线程。
protected Collection< Thread> getQueuedThreads()- // 返回正在等待获取的线程的估计数目。
int getQueueLength()- // 查询是否有线程正在等待获取。
boolean hasQueuedThreads()- // 如果此信号量的公平设置为 true,则返回 true。
boolean isFair()- // 根据指定的缩减量减小可用许可的数目。
protected void reducePermits(int reduction)- // 释放一个许可,将其返回给信号量。
void release()- // 释放给定数目的许可,将其返回到信号量。
void release(int permits)- // 返回标识此信号量的字符串,以及信号量的状态。
String toString()- // 仅在调用时此信号量存在一个可用许可,才从信号量获取许可。
boolean tryAcquire()- // 仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。
boolean tryAcquire(int permits)- // 如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。
boolean tryAcquire(int permits, long timeout, TimeUnit unit)- // 如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被中断,则从此信号量获取一个许可。
boolean tryAcquire(long timeout, TimeUnit unit)
三、使用
3.1 demo1
简单的示例代码,演示了如何使用 Semaphore:
public class SemaphoreExample {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); // 初始化一个许可证数量为2的 Semaphore
// 创建多个线程尝试获取许可证
for (int i = 1; i <= 5; i++) {
final int threadId = i;
new Thread(() -> {
try {
semaphore.acquire();
System.out.println("Thread " + threadId + " acquired the permit.");
Thread.sleep(2000); // 模拟线程在访问资源
semaphore.release();
System.out.println("Thread " + threadId + " released the permit.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
输出:呃,只看到了获取和释放信号量,好像并不能看出同时运行的线程数。
3.1 demo2
假设有30个人在超市支付结算,只有3个结算窗口,代码实现逻辑如下
public static void test3() {
// 排队总人数(请求总数)
int clientTotal = 30;
// 可同时结算商品的窗口数量(同时并发执行的线程数)
int threadTotal = 3;
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
for (int i = 0; i < clientTotal; i++) {
final int count = i;
executorService.execute(() -> {
try {
semaphore.acquire(1);
payment(count);
semaphore.release(1);
} catch (Exception e) {
e.printStackTrace();
}
executorService.shutdown();
}
private static void payment(int i) throws InterruptedException {
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss z");
Date date = new Date(System.currentTimeMillis());
System.out.println(Thread.currentThread().getName() + " 支付结算中" + formatter.format(date));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
输出:明显看到同一时间三个线程在运行,并且间隔两秒释放信号量。
四、适用场景
Semaphore 可以用于多种场景,例如:
连接池管理
: 在数据库连接池、线程池等资源池中,可以使用 Semaphore 来限制同时获取资源的线程数量,避免资源被过度占用。
并发访问控制
: 在多线程环境下,可以使用 Semaphore 控制同时访问共享资源的线程数量,确保线程安全性。
流量控制
: 在网络编程中,可以使用 Semaphore 来控制并发访问量,限制系统的并发连接数,防止系统被过度请求压垮。
生产者-消费者模式
: 在生产者-消费者模式中,可以使用 Semaphore 来控制生产者和消费者之间的同步,确保生产者和消费者之间的协调工作。
限流控制
: 在微服务架构中,可以使用 Semaphore 控制服务之间的调用频率,避免某个服务被过度调用而导致系统崩溃。
任务调度控制
: 在任务调度系统中,可以使用 Semaphore 控制同时执行的任务数量,避免系统资源被过度占用。
缓存控制
: 在缓存系统中,可以使用 Semaphore 控制对缓存的并发访问,避免缓存击穿和缓存雪崩等问题。
Semaphore 可以用于任何需要控制并发访问数量的场景,帮助实现线程安全和资源管理。在实际开发中,使用 Semaphore 可以提高系统的稳定性和性能。
参考链接:
java锁之Semaphore(信号量,限制并发数量)