/**
* A counting semaphore. Conceptually, a semaphore maintains a set of
* permits. Each {@link #acquire} blocks if necessary until a permit is
* available, and then takes it. Each {@link #release} adds a permit,
* potentially releasing a blocking acquirer.
* However, no actual permit objects are used; the {@code Semaphore} just
* keeps a count of the number available and acts accordingly.
*一个计数的信号量,从概念上说,一个信号维持了一组许可证。如果需要,每个acquire方法(需求方)都会阻碍住(相当于减一的操作),直到这个凭证是可用的,然后才能去得到它。每个release,都会增加一个凭证,意味着的会释放给一个阻碍的需求方。
* <p>Semaphores are often used to restrict the number of threads than can
* access some (physical or logical) resource. For example, here is
* a class that uses a semaphore to control access to a pool of items:
信号量通常被用在限制能够访问(物理或者逻辑)资源的线程数目。比如,这里的一个类使用一个信号量去控制访问池里面的元素。
* <pre> {@code
* class Pool {
* private static final int MAX_AVAILABLE = 100;
//定义一个资源限制,池中元素的数量。
* private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
*
* public Object getItem() throws InterruptedException {
//阻碍住一个许可证此时这个凭证不可用了。可以理解为一个资源占用。
* available.acquire();
* return getNextAvailableItem();
* }
*
* public void putItem(Object x) {
* if (markAsUnused(x))
//增加许可证。可用资源增加了。
* available.release();
* }
*
* // Not a particularly efficient data structure; just for demo
*
* protected Object[] items = ... whatever kinds of items being managed
* protected boolean[] used = new boolean[MAX_AVAILABLE];
*//可以理解下面模型为简单的标记清除模型。
* protected synchronized Object getNextAvailableItem() {
* for (int i = 0; i < MAX_AVAILABLE; ++i) {
* if (!used[i]) {
* used[i] = true;
* return items[i];
* }
* }
* return null; // not reached
* }
*
* protected synchronized boolean markAsUnused(Object item) {
* for (int i = 0; i < MAX_AVAILABLE; ++i) {
* if (item == items[i]) {
* if (used[i]) {
* used[i] = false;
* return true;
* } else
* return false;
* }
* }
* return false;
* }
* }}</pre>
*
* <p>Before obtaining an item each thread must acquire a permit from
* the semaphore, guaranteeing that an item is available for use. When
* the thread has finished with the item it is returned back to the
* pool and a permit is returned to the semaphore, allowing another
* thread to acquire that item. Note that no synchronization lock is
* held when {@link #acquire} is called as that would prevent an item
* from being returned to the pool. The semaphore encapsulates the
* synchronization needed to restrict access to the pool, separately
* from any synchronization needed to maintain the consistency of the
* pool itself.
*在获取元素之前,每个线程必须从信号量中获取许可,才能保证这个一个元素是可用的。
当这个线程用完了这个元素然后将它扔到池里,并将许可证返回给信号量,再让其他线程去获取这个元素。注意就是:
当acquire调用时是没有持有同步锁的,如果持有的话元素就不能回到池中。信号量封装了限制访问池所需的同步(本身继承的同步类使用的是AQS,啥是AQS?下章讲解),与维护池本身的一致性所需的任何同步是分开的。
* <p>A semaphore initialized to one, and which is used such that it
* only has at most one permit available, can serve as a mutual
* exclusion lock. This is more commonly known as a <em>binary
* semaphore</em>, because it only has two states: one permit
* available, or zero permits available. When used in this way, the
* binary semaphore has the property (unlike many {@link java.util.concurrent.locks.Lock}
* implementations), that the "lock" can be released by a
* thread other than the owner (as semaphores have no notion of
* ownership). This can be useful in some specialized contexts, such
* as deadlock recovery.
*初始化一个信号量可以被用来做互斥锁,因为该信号量最多有一个许可证被用。这个一般被称为二进制信号量,因为,它只有两种状态,
一个是许可证可以被用,一个是没有许可证被用。当我们使用这种方式的时候,这个二进制信号量的属性与其他的锁不一样。它可以由不是它的所持有者释放(因为信号量没有所有者这一个概念)。在一些指定的上下文中非常有用,如死锁恢复。
* <p> The constructor for this class optionally accepts a
* <em>fairness</em> parameter. When set false, this class makes no
* guarantees about the order in which threads acquire permits. In
* particular, <em>barging</em> is permitted, that is, a thread
* invoking {@link #acquire} can be allocated a permit ahead of a
* thread that has been waiting - logically the new thread places itself at
* the head of the queue of waiting threads. When fairness is set true, the
* semaphore guarantees that threads invoking any of the {@link
* #acquire() acquire} methods are selected to obtain permits in the order in
* which their invocation of those methods was processed
* (first-in-first-out; FIFO). Note that FIFO ordering necessarily
* applies to specific internal points of execution within these
* methods. So, it is possible for one thread to invoke
* {@code acquire} before another, but reach the ordering point after
* the other, and similarly upon return from the method.
* Also note that the untimed {@link #tryAcquire() tryAcquire} methods do not
* honor the fairness setting, but will take any permits that are
* available.
这个类的构造方法可以随意的接受一个fairness参数,当设置为false的时候,这个类将不会保证关于获取许可证的顺序。详细说明,barging是允许的,也就是说一个线程调用acquire方法能够在等待之前分配许可证,逻辑是,新的线程会将它自己放置在这个等待队列的最前端。当fairness设置为true的时候,这个信号量将保证任何一个线程调用任何一个acquire是被选中,以便按照处理这些方法调用的顺序获得许可。(这里主要讲了参数设置为TRUE和false的区别)。注意,这个FIFO排序必须引用在指定内部的执行节点。所以,一个线程有可能在另一个线程之前调用acquire,除非是一个接一个到达执行点,从方法返回也是如此。还需要注意的是不定时的tryAcquire方法不准守公平的设置,但会接受任何可用的许可。
*
* <p>Generally, semaphores used to control resource access should be
* initialized as fair, to ensure that no thread is starved out from
* accessing a resource. When using semaphores for other kinds of
* synchronization control, the throughput advantages of non-fair
* ordering often outweigh fairness considerations.
通常来说,信号量被用在控制资源的访问应该被初始化为fair。以确保没有任何线程在访问资源的时候陷入饥饿。
当用信号量控制其他类型的同步,公平排序通常比不公平吞吐量的优势的比重更加值得考虑。
* <p>This class also provides convenience methods to {@link
* #acquire(int) acquire} and {@link #release(int) release} multiple
* permits at a time. Beware of the increased risk of indefinite
* postponement when these methods are used without fairness set true.
这个类也提供了方便的方法acquire,release(int)一次多个许可证。当这些方法没有在公平设置为true的时候,当心会增加不确定延迟的风险。
* <p>Memory consistency effects: Actions in a thread prior to calling
* a "release" method such as {@code release()}
* <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* actions following a successful "acquire" method such as {@code acquire()}
* in another thread.
内存一致性的影响,调用前线程操作release()方法。动作允许一个成功的acquire方法在其他线程中。
* @since 1.5
* @author Doug Lea
*/
public class Semaphore implements java.io.Serializable {
private static final long serialVersionUID = -3222578661600680210L;
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
//这个操作为volatile的内存语义写
setState(permits);
}
final int getPermits() {
//这个操作为volatile的内存语义读
return getState();
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
//可用的减去获得的计算还剩多少
int remaining = available - acquires;
//AQS方法比较更新
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;
}
}
final void reducePermits(int reductions) {//获取许可
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
final int drainPermits() {//许可状态消耗完
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
/**
* NonFair version
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
/**
* Fair version
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
//构造方法,许可证个数,及采用公平和非公平策略
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
//从信号量获取一个许可,如果无可用许可前 将一直阻塞等待,
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//从信号量尝试获取一个许可,如果无可用许可,直接返回false,不会阻塞
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
//在指定的时间内尝试从信号量中获取许可,如果在指定的时间内获取成功,返回true,否则返回false
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//释放一个许可
public void release() {
sync.releaseShared(1);
}
//从信号量获取指定数目许可,如果无可用许可前 将一直阻塞等待,
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
//
public void acquireUninterruptibly(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireShared(permits);
}
//从信号量尝试获取一定数目许可,如果无可用许可,直接返回false,不会阻塞
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
}
//在指定的时间内尝试从信号量中获取一定数目许可,如果在指定的时间内获取成功,返回true,否则返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
}
//释放一定数量的许可
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
//获取当前信号量可用的许可,这个主要是用在判断现在可用的有多少个,主要用于调试。
public int availablePermits() {
return sync.getPermits();
}
//可获取并返回立即可用的所有许可个数,并且将可用许可置0。
// if (current == 0 || compareAndSetState(current, 0))
public int drainPermits() {
return sync.drainPermits();
}
//减少许可数量
protected void reducePermits(int reduction) {
if (reduction < 0) throw new IllegalArgumentException();
sync.reducePermits(reduction);
}
//判断当前采用的何种策略。
public boolean isFair() {
return sync instanceof FairSync;
}
//返回是否存在正在等待的线程。
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
//等待的数量
public final int getQueueLength() {
return sync.getQueueLength();
}
//等待的集合
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
//
public String toString() {
return super.toString() + "[Permits = " + sync.getPermits() + "]";
}
}
Semaphore运用例子,:
public void testSparkSubmitVmShutsDown() throws Exception {
ChildProcAppHandle handle = LauncherServer.newAppHandle();
TestClient client = null;
//产生一个非公平的semaphore
final Semaphore semaphore = new Semaphore(0);
try {
//定义一个网络连接
Socket s = new Socket(InetAddress.getLoopbackAddress(),
LauncherServer.getServerInstance().getPort());
//添加一个监听
handle.addListener(new SparkAppHandle.Listener() {
public void stateChanged(SparkAppHandle handle) {
semaphore.release();
}
public void infoChanged(SparkAppHandle handle) {
semaphore.release();
}
});
client = new TestClient(s);
client.send(new Hello(handle.getSecret(), "1.4.0"));
//尝试获取信号
assertTrue(semaphore.tryAcquire(30, TimeUnit.SECONDS));
// Make sure the server matched the client to the handle.
assertNotNull(handle.getConnection());
close(client);
assertTrue(semaphore.tryAcquire(30, TimeUnit.SECONDS));
assertEquals(SparkAppHandle.State.LOST, handle.getState());
} finally {
kill(handle);
close(client);
client.clientThread.join();
}
}