- 所有已实现的接口:
-
Serializable
acquire()在必要时阻塞,直到有许可证可用,然后获取它。每个release()添加一个许可证,可能释放一个阻塞的获取者。但是,实际上没有使用许可对象;信号量只是保持可用数量的计数,并相应地采取行动。
信号量通常用于限制可以访问某个(物理或逻辑)资源的线程数量。例如,这里是一个使用信号量来控制对项目池访问的类:
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();
}
// 不是特别高效的数据结构;仅供演示
protected Object[] items = ...; // 正在管理的各种项目
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; // 不会到达
}
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;
}
}
在获取项目之前,每个线程必须从信号量获取一个许可,以确保项目可供使用。当线程完成项目后,将其返回到池中,并将一个许可返回给信号量,允许另一个线程获取该项目。请注意,调用acquire()时不会持有同步锁,因为那样会阻止项目被返回到池中。信号量封装了限制对池的访问所需的同步,与维护池本身一致性所需的任何同步分开。
初始化为一的信号量,且仅具有最多一个许可证可用的用法称为互斥锁。这更常被称为二进制信号量,因为它只有两种状态:一个许可证可用,或零个许可证可用。当以这种方式使用时,二进制信号量具有一个特性(与许多Lock实现不同),即“锁”可以被除所有者之外的线程释放(因为信号量没有所有权概念)。这在某些专门的情况下很有用,例如死锁恢复。
此类的构造函数可选择接受一个公平性参数。当设置为false时,此类不对线程获取许可的顺序做任何保证。特别地,允许插队,即,调用acquire()的线程可以在等待的线程队列中被分配一个许可 - 逻辑上,新线程将自己放在等待线程队列的队首。当公平性设置为true时,信号量保证调用任何acquire方法的线程按照它们调用这些方法的顺序被选中获取许可(先进先出;FIFO)。请注意,FIFO排序必然适用于这些方法内部执行的特定点。因此,一个线程可能在另一个线程之前调用acquire,但在达到排序点之后,以及从方法返回时也是如此。还请注意,未定时的tryAcquire方法不遵守公平性设置,但会获取任何可用的许可。
通常,用于控制资源访问的信号量应该初始化为公平的,以确保没有线程被饿死无法访问资源。当将信号量用于其他类型的同步控制时,非公平排序的吞吐量优势通常超过公平性考虑。
此类还提供了方便的方法,以一次acquire和release多个许可。这些方法通常比循环更有效和更有效。但是,它们不建立任何优先顺序。例如,如果线程A调用s.acquire(3),线程B调用s.acquire(2),并且有两个许可证可用,则不能保证线程B将获得它们,除非其获取较早,并且信号量s处于公平模式。
内存一致性效果:在调用“release”方法(如release())之前的线程中的操作发生在另一个线程中成功调用“acquire”方法(如acquire())之后的操作。
- 自从:
- 1.5
- 参见:
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidacquire()从此信号量获取一个许可证,阻塞直到有一个可用,或线程被中断。voidacquire(int permits) 从此信号量获取给定数量的许可证,阻塞直到所有许可证都可用,或线程被中断。void从此信号量获取一个许可证,阻塞直到有一个可用。voidacquireUninterruptibly(int permits) 从此信号量获取给定数量的许可证,阻塞直到所有许可证都可用。int返回此信号量中当前可用的许可证数量。int获取并返回所有立即可用的许可证,或者如果有负许可证可用,则释放它们。protected Collection<Thread> 返回一个包含可能正在等待获取许可证的线程的集合。final int返回等待获取许可证的线程数的估计值。final boolean查询是否有任何线程正在等待获取许可证。booleanisFair()如果此信号量设置为公平,则返回true。protected voidreducePermits(int reduction) 将可用许可证数量减少指定的减少量。voidrelease()释放一个许可证,将其返回给信号量。voidrelease(int permits) 释放给定数量的许可证,将它们返回给信号量。toString()返回标识此信号量及其状态的字符串。boolean从此信号量获取一个许可证,仅当调用时有一个可用。booleantryAcquire(int permits) 从此信号量获取给定数量的许可证,仅当调用时所有许可证都可用。booleantryAcquire(int permits, long timeout, TimeUnit unit) 从此信号量获取给定数量的许可证,如果在给定等待时间内所有许可证都可用,并且当前线程未被中断。booleantryAcquire(long timeout, TimeUnit unit) 从此信号量获取一个许可证,如果在给定等待时间内有一个可用,并且当前线程未被中断。
-
Constructor Details
-
Semaphore
public Semaphore(int permits) 使用给定数量的许可证和非公平公平性设置创建一个Semaphore。- 参数:
-
permits- 可用的初始许可证数量。此值可能为负数,在这种情况下,必须在授予任何许可证之前发生释放。
-
Semaphore
public Semaphore(int permits, boolean fair) 使用给定数量的许可证和给定公平性设置创建一个Semaphore。- 参数:
-
permits- 可用的初始许可证数量。此值可能为负数,在这种情况下,必须在授予任何许可证之前发生释放。 -
fair- 如果此信号量将在争用时保证按照先进先出授予许可证,则为true,否则为false
-
-
Method Details
-
acquire
从此信号量获取一个许可证,阻塞直到有一个可用,或线程被中断。获取一个许可证,如果有一个可用并立即返回,将可用许可证数量减少一个。
如果没有许可证可用,则当前线程将为了线程调度目的而被禁用,并处于休眠状态,直到发生以下两种情况之一:
如果当前线程:
- 在进入此方法时设置了中断状态;或
- 在等待许可证时被中断,
InterruptedException,并清除当前线程的中断状态。- 抛出:
-
InterruptedException- 如果当前线程被中断
-
acquireUninterruptibly
public void acquireUninterruptibly() -
tryAcquire
public boolean tryAcquire()从此信号量获取许可,仅在调用时有许可可用时才获取。获取许可,如果有可用的许可并立即返回,则返回值为
true,将可用许可数减少一个。如果没有可用的许可,则此方法将立即返回值
false。即使此信号量已设置为使用公平排序策略,调用
tryAcquire()将立即获取一个许可,如果有可用的许可,无论其他线程当前是否在等待。这种“插队”行为在某些情况下可能很有用,尽管它破坏了公平性。如果要遵守公平设置,则使用tryAcquire(0, TimeUnit.SECONDS),它几乎等效(还会检测中断)。- 返回:
-
如果获取到许可则返回
true,否则返回false
-
tryAcquire
从此信号量获取许可,如果在给定的等待时间内有许可可用且当前线程尚未被中断。获取许可,如果有可用的许可并立即返回,则返回值为
true,将可用许可数减少一个。如果没有可用的许可,则当前线程将因线程调度目的而被禁用,并处于休眠状态,直到发生以下三种情况之一:
如果获取到许可则返回值
true。如果当前线程:
- 在进入此方法时已设置中断状态;或
- 在等待获取许可时被中断,
InterruptedException,并清除当前线程的中断状态。如果指定的等待时间过去,则返回值
false。如果时间小于或等于零,则该方法将根本不等待。- 参数:
-
timeout- 等待许可的最长时间 -
unit-timeout参数的时间单位 - 返回:
-
如果获取到许可则返回
true,如果等待时间过去而未获取到许可则返回false - 抛出:
-
InterruptedException- 如果当前线程被中断
-
release
public void release()释放一个许可,将其返回给信号量。释放一个许可,将可用许可数增加一个。如果有线程正在尝试获取许可,则会选择一个线程并给予刚释放的许可。该线程将被(重新)启用以进行线程调度。
没有要求释放许可的线程必须通过调用
acquire()来获取该许可。信号量的正确使用由应用程序中的编程约定确定。 -
acquire
从此信号量获取给定数量的许可,阻塞直到所有许可可用,或线程被中断。获取给定数量的许可,如果它们可用,则立即返回,将可用许可数减少给定数量。此方法的效果与循环
for (int i = 0; i < permits; ++i) acquire();相同,只是它一次性获取所有许可:如果可用许可不足,则当前线程将因线程调度目的而被禁用,并处于休眠状态,直到发生以下两种情况之一:
如果当前线程:
- 在进入此方法时已设置中断状态;或
- 在等待获取许可时被中断,
InterruptedException,并清除当前线程的中断状态。应分配给此线程的任何许可将分配给其他尝试获取许可的线程,就好像通过调用release()来提供了许可。- 参数:
-
permits- 要获取的许可数量 - 抛出:
-
InterruptedException- 如果当前线程被中断 -
IllegalArgumentException- 如果permits为负数
-
acquireUninterruptibly
public void acquireUninterruptibly(int permits) 从此信号量获取给定数量的许可,阻塞直到所有许可可用。获取给定数量的许可,如果它们可用,则立即返回,将可用许可数减少给定数量。此方法的效果与循环
for (int i = 0; i < permits; ++i) acquireUninterruptibly();相同,只是它一次性获取所有许可:如果可用许可不足,则当前线程将因线程调度目的而被禁用,并处于休眠状态,直到其他线程调用此信号量的一个
release方法,并且当前线程将被分配许可,可用许可数满足此请求。如果当前线程在等待许可时被中断,则它将继续等待,其在队列中的位置不受影响。当线程从此方法返回时,其中断状态将被设置。
- 参数:
-
permits- 要获取的许可数量 - 抛出:
-
IllegalArgumentException- 如果permits为负数
-
tryAcquire
public boolean tryAcquire(int permits) 从此信号量获取给定数量的许可,仅在调用时所有许可都可用时才获取。获取给定数量的许可,如果它们可用,则立即返回,返回值为
true,将可用许可数减少给定数量。如果可用许可不足,则此方法将立即返回值
false,并且可用许可数不变。即使此信号量已设置为使用公平排序策略,调用
tryAcquire将立即获取一个许可,如果有可用的许可,无论其他线程当前是否在等待。这种“插队”行为在某些情况下可能很有用,尽管它破坏了公平性。如果要遵守公平设置,则使用tryAcquire(permits, 0, TimeUnit.SECONDS),它几乎等效(还会检测中断)。- 参数:
-
permits- 要获取的许可数量 - 返回:
-
如果获取到许可则返回
true,否则返回false - 抛出:
-
IllegalArgumentException- 如果permits为负数
-
tryAcquire
从此信号量获取给定数量的许可,如果在给定的等待时间内所有许可都可用且当前线程尚未被中断。获取给定数量的许可,如果它们可用则立即返回,返回值为
true,将可用许可数减少给定数量。如果可用许可不足,则当前线程将因线程调度目的而被禁用,并处于休眠状态,直到发生以下三种情况之一:
如果获取到许可则返回值
true。如果当前线程:
- 在进入此方法时已设置中断状态;或
- 在等待获取许可时被中断,
InterruptedException,并清除当前线程的中断状态。应分配给此线程的任何许可将分配给其他尝试获取许可的线程,就好像通过调用release()来提供了许可。如果指定的等待时间过去,则返回值
false。如果时间小于或等于零,则该方法将根本不等待。应分配给此线程的任何许可将分配给其他尝试获取许可的线程,就好像通过调用release()来提供了许可。- 参数:
-
permits- 要获取的许可数量 -
timeout- 等待许可的最长时间 -
unit-timeout参数的时间单位 - 返回:
-
如果所有许可都被获取则返回
true,如果等待时间过去而未获取到所有许可则返回false - 抛出:
-
InterruptedException- 如果当前线程被中断 -
IllegalArgumentException- 如果permits为负数
-
release
public void release(int permits) 释放给定数量的许可证,将它们返回给信号量。释放给定数量的许可证,增加可用许可证的数量。如果有任何线程正在尝试获取许可证,则会选择一个线程并给予刚刚释放的许可证。如果可用许可证的数量满足该线程的请求,则该线程将被重新启用以进行线程调度;否则该线程将等待,直到有足够的许可证可用。如果在该线程的请求满足后仍然有许可证可用,则这些许可证将依次分配给其他正在尝试获取许可证的线程。
没有要求释放许可证的线程必须通过调用
acquire来获取该许可证。信号量的正确使用由应用程序中的编程约定确定。- 参数:
-
permits- 要释放的许可证数量 - 抛出:
-
IllegalArgumentException- 如果permits为负数
-
availablePermits
public int availablePermits()返回此信号量中当前可用的许可证数量。此方法通常用于调试和测试目的。
- 返回:
- 此信号量中当前可用的许可证数量
-
drainPermits
public int drainPermits()获取并返回所有立即可用的许可证,或者如果有负数的许可证可用,则释放它们。返回后,可用许可证数量为零。- 返回:
- 获取的许可证数量,或者如果为负数,则为释放的数量
-
reducePermits
protected void reducePermits(int reduction) 减少指定数量的可用许可证。此方法对于使用信号量跟踪变得不可用的资源的子类非常有用。此方法与acquire不同之处在于它不会阻塞等待许可证变为可用。- 参数:
-
reduction- 要移除的许可证数量 - 抛出:
-
IllegalArgumentException- 如果reduction为负数
-
isFair
public boolean isFair()如果此信号量设置为公平模式,则返回true。- 返回:
-
如果此信号量设置为公平模式,则返回
true
-
hasQueuedThreads
public final boolean hasQueuedThreads()查询是否有任何线程正在等待获取许可证。请注意,由于随时可能发生取消,因此返回true并不保证任何其他线程将获得许可证。此方法主要设计用于监视系统状态。- 返回:
-
如果可能有其他线程正在等待获取锁,则返回
true
-
getQueueLength
public final int getQueueLength()返回等待获取锁的线程数量的估计值。该值仅为估计值,因为在此方法遍历内部数据结构时,线程数量可能会动态变化。此方法设计用于监视系统状态,而不是用于同步控制。- 返回:
- 等待获取此锁的线程的估计数量
-
getQueuedThreads
返回一个包含可能正在等待获取许可证的线程的集合。由于在构造此结果时实际线程集可能会动态变化,因此返回的集合仅是尽力估计。返回的集合中的元素没有特定顺序。此方法设计用于便于构建提供更广泛监控功能的子类。- 返回:
- 线程集合
-
toString
返回一个标识此信号量及其状态的字符串。状态在括号中,包括字符串"Permits ="后跟许可证数量。
-