JUC Semaphore 信号量

概要

一个计数信号量,维护了一组许可。acquire() 方法在许可可用前将阻塞,许可可用时获取,每个release() 方法添加一个许可,潜在地释放一个阻塞的获取线程。

信号量常用于约束访问一些(物理或逻辑)资源的线程数量。

信号量初始化为 1 可以用作互斥锁,更常见的是称为二进制信号量,因为它只有两个状态:有一个许可可用,或没有许可可用。当以这种方式使用时,二进制信号量有个属性,“锁”可以被其他线程而不是属主线程(信号量没有记录属主关系)释放。

公平性属性:公平策略,按FIFO顺序分配许可;非公平策略,允许闯入,即 acquire 调用线程获取许可,而不是已经在等待的线程。

一般地,信号量用于控制资源的访问应当初始化为公平的,来保证没有线程因为不能访问资源而饥饿。当把信号量用于其他同步类型的控制,非公平顺序的吞吐量优势压过公平的考量。

内存一致性效果:线程调用 “release” 之前的动作 happens-before 于成功调用 “acquire” 的其他线程的后续动作。

实现

由于信号量也只是一个计数器,它可以允许多个线程获取许可,如果没有许可可用则阻塞获取线程,这些都跟共享模式下的AQS非常吻合。

用AQS的状态来表示许可的数量。

// 非公平策略下获取许可
final int nonfairTryAcquireShared( int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}

// 释放许可
protected final boolean tryReleaseShared (int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow
            throw new Error( "Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.