概述
Semaphore(信号量),通常用于限制某些资源的访问线程数量。通过调用aquire方法获取许可,没有获取到许可则阻塞当前线程直到获取到许可为止。操作完成后通过调用release方法来释放许可,从而允许其他线程去获取许可。同时Semaphore也支持超时的获取方式。
从描述来看这基本和AQS的原理和使用方式如出一辙,只是将同步状态设置了一个上界。
使用
假设一个教室只允许5个学生进入,当教室满了之后其他想进入的学生只能等待某个学生出来才能进入。
public class SemaphoreDemo {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 10; i++){
final int num = i;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("student" + num + " got in");
Thread.sleep(1000);
System.out.println("student" + num + " got out");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//为了使前五个线程不在同一时间点结束
Thread.sleep(100);
}
}
}
输出结果:
student0 got in
student1 got in
student2 got in
student3 got in
student4 got in
student0 got out
student5 got in
student1 got out
student6 got in
student2 got out
student7 got in
student3 got out
student8 got in
student4 got out
student9 got in
student5 got out
student6 got out
student7 got out
student8 got out
student9 got out
实现原理
Semaphore的结构
看Semaphore的目录结构就基本可以明白Semaphore的实现基本也是通过AQS初始化一个state,并通过获取同步状态和释放同步状态的方式去控制访问量,如果tryAquire方方法失败意味着获取许可失败,此时AQS将挂起当前的线程。同时调用release来释放同步状态。
实现方式基本比较简单,同时还支持了公平和非公平的选择。
值得注意的是Semaphore还提供了一个drainPermits方法,作用是获取并返回立即可用的所有许可。
例子:
public class SemaphoreDemo {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 10; i++){
final int num = i;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
if (num == 3)
semaphore.drainPermits();
semaphore.acquire();
System.out.println("student" + num + " got in");
Thread.sleep(1000);
System.out.println("student" + num + " got out");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//为了使前五个线程不在同一时间点结束
Thread.sleep(100);
}
}
}
在第四次获取之前将剩下的许可全部获取
输出:
student0 got in
student1 got in
student2 got in
student0 got out
student3 got in
student1 got out
student4 got in
student2 got out
student5 got in
student3 got out
student6 got in
student4 got out
student7 got in
student5 got out
student8 got in
student6 got out
student9 got in
student7 got out
student8 got out
student9 got out
源码:
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
自旋+CAS的方式将同步状态设置为0(即没有可用许可的状态)