计数信号量(Counting Semaphore)用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界。

Semaphore中管理着一组虚拟的许可(permit),许可的初始数量可通过构造函数来指定。 在执行操作时可以首先获得许可(只要还有剩余的许可):Semaphore.acquire()。并在使用以后释放许可:Semaphore.release()。如果没有许可 acquire将阻塞直到许可或者直到被中断,操作超时。

Semaphore可以用于实现资源池,例如数据库连接池。我们可以构造一个固定长度的资源池,当池为空时,请求资源将会失败,但你真正希望看到的行为是阻塞而不是失败,并且当池非空时接触阻塞。

/**
 * 实现一个HashSet,最多只能添加bound个元素,超出后再添加,需要等待,直到map中删除了一个元素
 */
public class BoundedHashSet<T> {
    private final Set<T> set;
    private final Semaphore sem;

    //初始化map的大小
    public BoundedHashSet(int bound) {
        this.set = Collections.synchronizedSet(new HashSet<>());
        this.sem = new Semaphore(bound);
        /*
        Semaphore还有一个构造方法
        public Semaphore(int permits, boolean fair)
        fair表示 阻塞在外面的线程是否排队进入。
         */
    }

    public boolean add(T o) throws InterruptedException {
        //获得许可,如果添加不成功,则释放许可
        sem.acquire();
        boolean wasAdded = false;
        try {
            wasAdded = set.add(o);
            return wasAdded;
        } finally {
            if (!wasAdded) {
                sem.release();
            }

        }
    }

    public boolean remove(Object o) {
        boolean wasRemoved = set.remove(o);
        //成功删除元素后,释放一个许可
        if (wasRemoved) {
            sem.release();
        }
        return wasRemoved;
    }

}