信号量Semaphore
About 3 min
正常的锁不管是内部锁synchronized
还是重入锁ReentrantLock
在任意时刻只允许一个线程访问临界资源,Java中有没有可以让多个任务同时这个临界资源呢?接下来就让我们来看下线程同步的辅助类Semaphore
。
Semaphore使用
信号量Semaphore
为多线程协作提供了更为强大的控制方法,它可以指定多个线程同时访问某个资源。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
参数的信号量是非公平的,即多个线程同时去获取许可,哪个线程会拿到是不确定的。而当fair
为true时,信号量是公平的,即多个线程同时去获取许可,是按照FIFO队列
的规则来选的线程。
Semaphore
中主要的方法有:
public void acquire()
public void acquireUninterruptibly()
public boolean tryAcquire()
public boolean tryAcquire(long timeout, TimeUnit unit)
public void release()
acquire
: 获得一个准入的许可。若无法获得,则线程会等待直到有其它线程释放一个许可或者当前线程被中断acquireUninterruptibly
: 和acquire
方法类似,区别在于它不响应中断tryAcquire
: 尝试获得一个许可,如果成功返回true,失败则返回false,它不会进行等待而是立即返回。带参数的tryAcquire
区别在于它会等待指定的时间release
: 用于资源访问结束后释放一个许可
当然上述方法都有带permits
额外参数的重载方法,表示一次性可以操作多个许可,这比循环去获取或者释放的方式更加的高效。
public boolean acquire(int permits)
...
public void release(int permits)
下面我们就举个银行业务办理的例子来说明Semaphore
的用法:
public class SemaphoreTest {
final static Semaphore availableWindow = new Semaphore(2);
public static void main(String[] args) {
var runnable = new Runnable() {
int count = 1;
@Override
public void run() {
int time = (int) (Math.random() * 10 + 3);
int num = count++;
try {
availableWindow.acquire();
System.out.println("正在为第【" + num + "】个客户办理业务,需要时间:" + time + "s!");
Thread.sleep(time * 1000L);
if (availableWindow.hasQueuedThreads()) {
System.out.println("第【" + num + "】个客户已办理完业务,有请下一位!");
} else {
System.out.println("第【" + num + "】个客户已办理完业务,没有客户了,休息中!");
}
availableWindow.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for (int i = 1; i < 5; i++) {
new Thread(runnable).start();
}
}
}
输出不确定,某次的结果如下:
正在为第【1】个客户办理业务,需要时间:3s!
正在为第【2】个客户办理业务,需要时间:11s!
第【1】个客户已办理完业务,有请下一位!
正在为第【4】个客户办理业务,需要时间:7s!
第【4】个客户已办理完业务,有请下一位!
正在为第【3】个客户办理业务,需要时间:5s!
第【2】个客户已办理完业务,没有客户了,休息中!
第【3】个客户已办理完业务,没有客户了,休息中!
这里我们创建五个线程来模拟五个用户去银行办理业务,其中Semaphore的许可个数设置为2,表示银行业务办理窗口的数量,也就是说同时办理业务的人数最大是2。
Semaphore的
hasQueuedThreads
方法查询是否有线程正在等待获取许可。
从上述的输出结果得知:银行有两个窗口,一开始客户1
和客户2
得到了办理的机会,3秒之后客户1
办理完释放了窗口。于是客户4
得到机会去办理业务,由于客户4
业务比较简单只花了7秒钟就完成了。于是这个窗口被释放,客户3
得到了办理的机会。最后客户2
和客户3
办理完业务。