计数信号量用来控制同时访问某个资源的操作数量。还可以用来实现某种资源池,例如消费者和生产者之间的控制,例如生产者向容器中添加元素,消费者删除元素,容器是有边界的。
Semaphore总管理着一组虚拟的许可,许可的数量可以在构建初始化信号量的时候设定
Semaphore sem= new Semaphore(3);
数量可以是容器的边界或者是线程池的线程数量。在如容器内添加元素时,要先获取Semaphore的一个许可 sem.acquire(),如果容器的元素已经满了,sem.acquire()会阻塞,当容器中删除了一个元素,就释放一个许可。
下面可以实现一个带边界的容器BoundedHashSet
public class BoundedHashSet<T> {
private final Set<T> set;
private final Semaphore sem;
public BoundedHashSet(int bound){
this.set=Collections.synchronizedSet(new HashSet<T>());
sem=new Semaphore(bound);
}
public boolean add(T o) throws InterruptedException{
long start=System.currentTimeMillis();
System.out.println("开始添加元素");
sem.acquire();
boolean wasAdded=false;
try {
wasAdded=set.add(o);
System.out.println("添加成功");
for (Iterator i=set.iterator();i.hasNext();) {
String string=(String)i.next();
System.out.println(string);
}
System.out.println(System.currentTimeMillis()-start);
return wasAdded;
} finally{
if(!wasAdded){
sem.release();
System.out.println("添加失败");
}
}
}
public boolean remove(Object o){
boolean wasRemoved=set.remove(o);
if(wasRemoved){
sem.release();
System.out.println("释放元素:"+o);
}
return wasRemoved;
}
}
测试类,定义一个要向容器边界为3的容器添加的数组元素,一个线程添加元素,一个线程删除元素
public class sem {
public static void main(String[] args) {
final BoundedHashSet<String> boundedHashSet=new BoundedHashSet<>(3);
final String[] timStrings={"a","b","c","d","e","f"};
Thread addThread=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
int count=1;
for (String string : timStrings) {
Thread.sleep(1000);
if(boundedHashSet.add(string)){
System.out.println("添加成功第"+count+"个"+string);
}
count++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
addThread.start();
Thread releaseThread=new Thread(new Runnable() {
@Override
public void run() {
int count=1;
try {
for (String string : timStrings) {
if(count==1){
Thread.sleep(6000);
}else{
Thread.sleep(1500);
}
boundedHashSet.remove(string);
System.out.println("删除第"+count+"元素");
count++;
}
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
releaseThread.start();
}
}
可以看下运行结果,在线程添加第四个元素时,要等其它线程删除元素,所以阻塞了1998毫秒的时间