一、Semaphore类简介
Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。
通常用于那些资源有明确访问数量限制的场景,常用于限流。
比如:数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。
二、Semaphore类说明
1. Semaphore类结构
2. Semaphore类方法
-
acquire
作用:获取一个Permit,调用该方法的线程将阻塞直到此线程成功获取到一个Permit
或此线程被其他线程Interrupt
-
acquire(int)
作用:获取多个Permit,调用该方法的线程将阻塞直到此线程成功获取到指定数量的Permit
或此线程被其他线程Interrupt
-
acquireUninterruptibly
作用:获取一个Permit,调用此方法的线程阻塞直到成功获取到一个Permit
。当有其他线程要Interrupt正在等待Permit的此线程,此线程不会修改中断标志而是继续等待Permit。在成功等待到信号量后,此线程的中断标志才会被改变。 -
acquireUninterruptibly(int)
作用:作用同acquireUninterruptibly,区别在于此方法要求获取到多个Permit。 -
tryAcquire , tryAcquire(long,TimeUnit) , tryAcquire(int) , tryAcquire(int,long,TimeUnit)
作用:尝试获取一个Permit,调用后立即返回而不是阻塞等待Permit。 获取结果以布尔值返回,获取成功则返回true,否则返回false。 -
release , release(int)
作用:释放一个或多个Permit,可用Permit数增加。分配释放的Permit给等待Permit的阻塞线程。需要注意,调用release的线程不一定要曾acquire过 -
availablePermits
作用:获取当前Semaphore剩余的Permit数量。 -
drainPermits
作用:获取并返回当前所有立即可用的Permit。 -
reducePermits(int)
作用:减少指定数量的Permit,此方法在使用信号量跟踪变得不可用的资源的子类中很有用。此方法与acquire不同之处在于它不会阻塞等待直到有可用Permit。 -
isFair
作用:判断当前Semaphore是否是公平模式,即FIFO模式。 -
hasQueuedThreads
作用:判断当前是否存在线程因为acquire获取不到Permit而阻塞,如果有则返回true。但由于线程状态是在任何时候都有可能改变,因此无法保证获取结果并返回结果这个过程中线程状态是否变化,从而导致当前上下文结果与方法调用返回结果存在差异的情况发生。 -
getQueueLength
作用:获取当前调用acquire阻塞的线程数量
。只能作为一个预估值,原因同hasQueuedThreads方法。 -
getQueuedThreads
作用:获取当前调用acquire阻塞的线程集合
。只能作为一个预估值,原因同hasQueuedThreads方法。
三.使用例子
Random类使用CAS实现线程安全,但线程数量多的话,CAS失败率大大提高,因此这里使用更适合高并发场景下的ThreadLocalRandom。
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadLocalRandom;
/**
* 模拟限流,10桌人进餐厅吃饭,每次只能服务1桌人
* 每桌用餐时间随机,但在1~10s内
*/
public class SemaphoreDemo {
private static final Semaphore semaphore = new Semaphore(1);
public static void main(String[] args) {
Thread thread = new Thread(()->{
ThreadLocalRandom random = ThreadLocalRandom.current();
try {
// 获取并保持Permit
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 线程成功获取了一个Permit");
// 线程休眠1~10s,sleep不释放资源,只让出CPU
Integer time = random.nextInt(1,10);
Thread.sleep(time * 1000);
System.out.println(Thread.currentThread().getName() + " 用餐时间" + time + "s");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " 线程被中断");
e.printStackTrace();
}finally {
// 释放Permit
semaphore.release();
System.out.println(Thread.currentThread().getName() + " 线程释放了一个Permit");
}
});
// 生成10个线程并执行
for(int i = 1 ; i <= 10 ; i++){
new Thread(thread,"服务单号" + i ).start();
}
}
}
参考资料:
https://www.jianshu.com/p/1a3eb2d34c42
https://www.jb51.net/article/209086.htm