一.概念
Semaphore
(信号量)可以用来控制同时访问特定资源的线程数量,常用于限流场景。Semaphore
支持公平锁和非公平锁。
Semaphore
接收一个int
整型值,表示 许可证数量, 如果许可证剩余数量大于零时,线程则允许访问该共享资源;如果许可证剩余数量为零时,则拒绝线程访问该共享资源。 Semaphore所维护的许可证数量就是允许访问共享资源的最大线程数量。 所以,线程想要访问共享资源必须从Semaphore中获取到许可证。
二.原理
Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理。Semaphore在构造时,需要传入许可证的数量,它最后传递给了AQS的state值。线程在调用acquire方法获取许可证时,如果Semaphore中许可证的数量大于0,许可证的数量就减1,线程继续运行,当线程运行结束调用release方法时释放许可证时,许可证的数量就加1。如果获取许可证时,Semaphore中许可证的数量为0,则获取失败,线程进入AQS的等待队列中,等待被其它释放许可证的线程唤醒。
三.示例
假定商场的卫生间一共有三个位置,所以超过三个人时,后面的需要排队,当有位置空出是,后面进行补位。。。
package com.example.demo;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.Semaphore;
public class Person implements Runnable {
private Semaphore wc;
private String name;
public Person(Semaphore wc, String name) {
this.wc = wc;
this.name = name;
}
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
Random random = new Random();
wc.acquire();
System.out.println(
sdf.format(new Date()) + " " + name + " 进卫生间...");
Thread.sleep((long) (random.nextDouble() * 5000) + 2000);
System.out.println(
sdf.format(new Date()) + " " + name + " 出卫生间!");
wc.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
public class SemaphoreTester {
public static void main(String[] args) throws InterruptedException {
//卫生间只有三个位置,所以初始化许可证的总数为3。
Semaphore washbasin = new Semaphore(3);
List<Thread> threads = new ArrayList<>(6);
threads.add(new Thread(new Person(washbasin, "小明")));
threads.add(new Thread(new Person(washbasin, "小阳")));
threads.add(new Thread(new Person(washbasin, "小张")));
threads.add(new Thread(new Person(washbasin, "小赵")));
threads.add(new Thread(new Person(washbasin, "小强")));
threads.add(new Thread(new Person(washbasin, "小超")));
for (Thread thread : threads) {
thread.start();
Thread.sleep(50);
}
}
}
结果: