1、介绍
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,通过协调各个线程以保证合理地使用公共资源。
Semaphore通过使用计数器来控制对共享资源的访问。 如果计数器大于0
,则允许访问。 如果为0,则拒绝访问。 计数器所计数的是允许访问共享资源的许可。 因此,要访问资源,必须从信号量中授予线程许可。
2、主要方法
-
void acquire()
:从信号量获取一个许可,如果无可用许可前将一直阻塞等待, -
void acquire(int permits)
:获取指定数目的许可,如果无可用许可前也将会一直阻塞等待 -
boolean tryAcquire()
:从信号量尝试获取一个许可,如果无可用许可,直接返回false,不会阻塞 -
boolean tryAcquire(int permits)
: 尝试获取指定数目的许可,如果无可用许可直接返回false -
boolean tryAcquire(int permits, long timeout, TimeUnit unit)
:
在指定的时间内尝试从信号量中获取许可,如果在指定的时间内获取成功,返回true,否则返回false -
void release()
:释放一个许可,别忘了在finally中使用,注意:多次调用该方法,会使信号量的许可数增加,达到动态扩展的效果,如:初始permits为1,调用了两次release,最大许可会改变为2 -
int availablePermits()
: 获取当前信号量可用的许可
Semaphore构造函数
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
permits
初始许可数,也就是最大访问线程数fair
当设置为false时,创建的信号量为非公平锁;当设置为true时,信号量是公平锁
关于java非公平锁和公平锁可以看这篇文章:一文搞懂java中的锁
3、Semaphore登录限流示例
在以下示例中,实现一个简单的登录队列,通过Semaphore
来限制系统中的用户数:
public static void main(String[] args) {
//允许最大的登录数
int slots=10;
ExecutorService executorService = Executors.newFixedThreadPool(slots);
LoginQueueUsingSemaphore loginQueue = new LoginQueueUsingSemaphore(slots);
//线程池模拟登录
for (int i = 1; i <= slots; i++) {
final int num=i;
executorService.execute(()->{
if (loginQueue.tryLogin()){
System.out.println("用户:"+num+"登录成功!");
}else {
System.out.println("用户:"+num+"登录失败!");
}
});
}
executorService.shutdown();
System.out.println("当前可用许可证数:"+loginQueue.availableSlots());
//此时已经登录了10个用户,再次登录的时候会返回false
if (loginQueue.tryLogin()){
System.out.println("登录成功!");
}else {
System.out.println("系统登录用户已满,登录失败!");
}
//有用户退出登录
loginQueue.logout();
//再次登录
if (loginQueue.tryLogin()){
System.out.println("登录成功!");
}else {
System.out.println("系统登录用户已满,登录失败!");
}
}
class LoginQueueUsingSemaphore{
private Semaphore semaphore;
/**
*
* @param slotLimit
*/
public LoginQueueUsingSemaphore(int slotLimit){
semaphore=new Semaphore(slotLimit);
}
boolean tryLogin() {
//获取一个凭证
return semaphore.tryAcquire();
}
void logout() {
semaphore.release();
}
int availableSlots() {
return semaphore.availablePermits();
}
}
运行结果: