信号量是一个并发工具类,(重复)是一个工具类,控制并发线程数,内部保存有虚拟的许可数量,线程执行操作时候要先获得许可,执行完毕释放许可,如果没有可用许可,则阻塞直到其它线程释放。
init(也就是new Semaphore)方法进行许可数的初始化
release方法释放许可
Acquire方法申请许可,元子操作
假设有个临界资源A
每次只允许一个线程访问,所以许可初始化为1,利用信号量就可以做隔离
线程t1和t2都要访问临界资源A
t1,t2都进行acquire,元子操作许可-1,t1获得许可,t2阻塞
t1执行完了,释放许可,唤醒t2进行操作
信号量还允许指定数量的多个线程进行访问一个临界区,这一点就是类似于限流器,注意在许可数量为0时,信号量是每释放一个许可就唤醒一条线程,线程被唤醒后直接执行任务,不会再判断许可(默认有一个许可释放),这样的好处是节约了唤醒多个线程又进行一轮许可争抢和判断的开销
这里用个抢厕所坑位的例子,十个人,三个厕所坑位,只能一个拉完下一个才能过来拉
public static void main(String[] args) throws Exception {
// 抢厕所 (许可数量,是否公平获取,公平则顺序唤醒,非公平随机唤醒)
Semaphore semaphore = new Semaphore(3, true);
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int customer = i;
Runnable run = () -> {
try {
// 获取许可(抢到厕所)
semaphore.acquire();
System.out.println("客人"+customer+"抢到厕所");
// 正在如厕(时间不定)
Thread.sleep((long) (Math.random() * 10000));
// 拉完释放
semaphore.release();
System.out.println("客人"+customer+"拉完了");
} catch (Exception e){}
};
executor.execute(run);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 退出线程池
executor.shutdown();
}