一.什么是Semaphore
Semaphore中管理一组虚拟的许可(permit),许可的初始数量可以通过构造函数指定。在操作时可以首先获得许可(只要还有剩余的许可),并且在使用以后释放许可。如果没有许可,那么acquire将阻塞直到有许可(或者直到被中断或者操作超时)。release方法将返回一个许可给信号量。
------ 摘自《Java并发编程实战》
在《Java并发编程实战》有个例子,在类中 BoundHashSet中,把信号量作为set集合的边界使用。在使用set的add方法前,我们会去像信号量获取许可(permit),有则会添加到set,没有则会一直阻塞(CAS,自旋锁)。注意,许可的数量与set限定的长度一致。
这里的场景:当超过指定的set集合长度后,不再能够继续插入,而且持续阻塞,一直等待到有线程在把“许可”归还后,获取“许可”继续执行。
BoundHashSet类
public class BoundHashSet<T> {
private final Set<T> set;
private final Semaphore sem;
//限定set的容量和信号量许可的数量
public BoundHashSet(int bound){
this.set = Collections.synchronizedSet(new HashSet<>(bound));
this.sem = new Semaphore(bound);
}
public boolean add(T o) throws InterruptedException {
//获取“许可”,没有获取到会阻塞
sem.acquire();
boolean wasAdded = false;
try {
System.out.println("正在添加 "+o+"并获得许可");
wasAdded = set.add(o);
return wasAdded;
}finally {
if (!wasAdded){
//没有添加成功,则释放信号量
sem.release();
}
}
}
public boolean remove(Object o){
boolean wasRemoved = set.remove(o);
if (wasRemoved){
System.out.println("正在移除 "+o+"并释放许可");
//移除成功元素,释放信号量
sem.release();
}
return wasRemoved;
}
@Override
public String toString() {
return Arrays.toString(set.toArray());
}
}
BoundHashSet测试类
public class ThreadTest18 {
public static void main(String[] args) throws InterruptedException {
BoundHashSet<String> set = new BoundHashSet<>(5);
//模拟添加线程
Thread t1 = new Thread(() -> {
try {
set.add("123");
set.add("abc");
set.add("1234");
set.add("12345");
set.add("123456");
//模拟添加第6元素,此时程序会一直阻塞,只要有别的许可放出
set.add("1234567");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//模拟移除线程
Thread t2 = new Thread(() -> {
try {
//假设这里需要花时间
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//假设有一条别的线程进来访问了这个set,并移除了"abc",释放了一个许可
set.remove("abc");
});
t1.start();
t2.start();
//join只为打印最后结果
t1.join();
t2.join();
//打印最后结果 "1234567"此时就可以拿到许可,然后插入成功
System.out.println(set);
}
}
1.模拟两条线程t1,t2
2.t1线程想要写入超过边界值的数据(例子中边界值是5,即5个“许可”)
3.t1在添加第六个元素的时候,会一直阻塞
4.t2线程花费了1500毫秒时间,然后开始把其中一个元素移除,这里释放一个“许可”
5.最后t2线程完成,然后t1线程终于拿到了一个“许可”,把"1234567"添加到set中
6.最后打印结果
[12345, 123, 1234, 123456, 1234567]