- java.lang.Object
-
- java.util.concurrent.Semaphore
-
- 实现的所有接口
-
Serializable
public class Semaphore extends Object implements Serializable
计数信号量。 从概念上讲,信号量保持一组许可。 如果有必要,每个acquire()
都会阻止,直到有许可证,然后接受。 每个release()
都添加了许可证,可能会释放阻止收购者。 但是,没有使用实际的许可对象;Semaphore
只保留可用数量并相应地采取行动。信号量通常用于限制线程数,而不是访问某些(物理或逻辑)资源。 例如,这是一个使用信号量来控制对项池的访问的类:
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; } }
在获取项目之前,每个线程必须从信号量获取许可证,以保证项目可供使用。 当线程完成项目后,它将返回到池中,并且许可证将返回到信号量,允许另一个线程获取该项目。 请注意,调用
acquire()
时不会保持同步锁定,因为这会阻止项目返回到池中。 信号量封装了限制访问池所需的同步,与维护池本身一致性所需的任何同步分开。信号量初始化为1,并且使用的信号量最多只有一个许可证可用作互斥锁。 这通常被称为二进制信号量 ,因为它只有两种状态:一种是可用的,或者是零可用的。 当以这种方式使用时,二进制信号量具有属性(与许多
Lock
实现不同),“锁”可以由除所有者之外的线程释放(因为信号量没有所有权的概念)。 这在某些特定的上下文中很有用,例如死锁恢复。此类的构造函数可选择接受公平参数。 设置为false时,此类不保证线程获取许可的顺序。 特别是, 闯入是允许的,也就是说,一个线程调用
acquire()
可以提前已经等待线程分配的许可证-在等待线程队列的头部逻辑新的线程将自己。 当公平性设置为true时,信号量保证选择调用任何acquire
方法的线程以按照它们调用这些方法的顺序获得许可(先进先出; FIFO)。 请注意,FIFO排序必然适用于这些方法中的特定内部执行点。 因此,一个线程可以在另一个线程之前调用acquire
,但是在另一个线程之后到达排序点,并且类似地从该方法返回时。 另请注意,不定时的tryAcquire
方法不遵守公平性设置,但会采取任何可用的许可。通常,用于控制资源访问的信号量应初始化为公平,以确保没有线程缺乏访问资源。 当将信号量用于其他类型的同步控制时,非公平排序的吞吐量优势通常超过公平性考虑。
此类还为
acquire
和release
一次提供多种许可的便捷方法。 这些方法通常比循环更有效和有效。 但是,他们没有建立任何优先顺序。 例如,如果线程A调用s.acquire(3
)并且线程B调用s.acquire(2)
,并且两个许可证变为可用,那么无法保证线程B将获得它们,除非它获得第一个并且Semaphores
处于公平模式。内存一致性效果:在另一个线程中成功“获取”方法(例如
acquire()
之后调用“释放”方法(例如release()
happen-before)acquire()
的线程中的操作。- 从以下版本开始:
- 1.5
- 另请参见:
- Serialized Form
-
-
方法摘要
所有方法 实例方法 具体的方法 变量和类型 方法 描述 void
acquire()
从此信号量获取许可,阻止直到一个可用,或者线程为 interrupted 。void
acquire(int permits)
从此信号量获取给定数量的许可,阻塞直到所有可用,或者线程为 interrupted 。void
acquireUninterruptibly()
从此信号量获取许可,阻止直到有一个可用。void
acquireUninterruptibly(int permits)
从此信号量获取给定数量的许可,阻塞直到所有许可都可用。int
availablePermits()
返回此信号量中可用的当前许可数。int
drainPermits()
获取并返回所有可立即获得的许可,或者如果有负许可,则将其释放。protected Collection<Thread>
getQueuedThreads()
返回包含可能正在等待获取的线程的集合。int
getQueueLength()
返回等待获取的线程数的估计值。boolean
hasQueuedThreads()
查询是否有任何线程正在等待获取。boolean
isFair()
如果此信号量的公平性设置为true,则返回true
。protected void
reducePermits(int reduction)
通过指定的减少量缩减可用许可证的数量。void
release()
发布许可证,将其返回到信号量。void
release(int permits)
释放给定数量的许可,将它们返回到信号量。String
toString()
返回标识此信号量及其状态的字符串。boolean
tryAcquire()
只有在调用时有一个许可证,才能从此信号量获取许可证。boolean
tryAcquire(int permits)
只有在调用时所有许可都可用时,才从此信号量获取给定数量的许可。boolean
tryAcquire(int permits, long timeout, TimeUnit unit)
如果在给定的等待时间内所有许可都可用且当前线程不是 interrupted ,则从此信号量获取给定数量的许可。boolean
tryAcquire(long timeout, TimeUnit unit)
如果在给定的等待时间内有可用的并且当前线程不是 interrupted ,则从该信号量获取许可。
-
-
-
构造方法详细信息
-
Semaphore
public Semaphore(int permits)
使用给定数量的许可和非公平公平设置创建Semaphore
。- 参数
-
permits
- 可用的初始许可数量。 此值可能为负值,在这种情况下,必须在授予任何获取之前发布。
-
Semaphore
public Semaphore(int permits, boolean fair)
使用给定的许可数和给定的公平性设置创建Semaphore
。- 参数
-
permits
- 可用的初始许可数量。 此值可能为负值,在这种情况下,必须在授予任何获取之前发布。 -
fair
-true
如果此信号量将保证在争用中先进先出授予许可,否则false
-
-
方法详细信息
-
acquire
public void acquire() throws InterruptedException
从此信号量获取许可,阻止直到一个可用,或者线程为interrupted 。获得许可证(如果有)并立即返回,将可用许可证数量减少一个。
如果没有可用的许可证,那么当前线程将被禁用以进行线程调度,并且在发生以下两种情况之一之前处于休眠状态:
- 其他一些线程为此信号量调用
release()
方法,然后为当前线程分配许可证; 要么 - 一些其他线程interrupts当前线程。
如果当前线程:
- 在进入此方法时设置其中断状态; 要么
- 在等待许可证时是interrupted ,
InterruptedException
并清除当前线程的中断状态。- 异常
-
InterruptedException
- 如果当前线程被中断
- 其他一些线程为此信号量调用
-
acquireUninterruptibly
public void acquireUninterruptibly()
从此信号量获取许可,阻止直到有一个可用。获得许可证(如果有)并立即返回,将可用许可证数量减少一个。
如果没有可用的许可证,则当前线程将被禁用以进行线程调度,并且在其他线程调用此信号量的
release()
方法并且接下来为当前线程分配许可证之前处于休眠状态。如果当前线程在等待许可时是interrupted ,那么它将继续等待,但是线程被分配许可的时间与它没有发生中断时收到许可的时间相比可能会发生变化。 当线程从该方法返回时,将设置其中断状态。
-
tryAcquire
public boolean tryAcquire()
只有在调用时有一个许可证,才能从此信号量获取许可证。获取许可证(如果有)并立即返回,值为
true
,将可用许可证数量减少一个。如果没有许可证,则此方法将立即返回值
false
。即使已将此信号量设置为使用公平排序策略,对
tryAcquire()
的调用tryAcquire()
将立即获得许可,如果有可用,无论其他线程当前是否正在等待。 这种“闯入”行为在某些情况下可能有用,即使它违反了公平性。 如果你想尊重公平性设置,那么使用几乎相同的tryAcquire(0, TimeUnit.SECONDS)
(它也会检测到中断)。- 结果
-
true
如果被收购许可证和false
,否则
-
tryAcquire
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
如果在给定的等待时间内有一个许可证并且当前线程不是interrupted ,则从该信号量获取许可证。获取许可证(如果有)并立即返回,值为
true
,将可用许可证数量减少一个。如果没有可用的许可证,那么当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:
- 其他一些线程为此信号量调用
release()
方法,然后为当前线程分配许可证; 要么 - 其他一些线程interrupts当前线程; 要么
- 指定的等待时间过去了。
如果获得许可,则返回值
true
。如果当前线程:
- 在进入此方法时设置其中断状态; 要么
- 在等待领取许可证时是interrupted ,
InterruptedException
并清除当前线程的中断状态。如果指定的等待时间过去,则返回值
false
。 如果时间小于或等于零,则该方法将不会等待。- 参数
-
timeout
- 等待许可的最长时间 -
unit
-timeout
参数的时间单位 - 结果
-
true
如果获得许可证)和false
如果在获得许可证之前等待时间已过去) - 异常
-
InterruptedException
- 如果当前线程被中断
- 其他一些线程为此信号量调用
-
release
public void release()
发布许可证,将其返回到信号量。发布许可证,将可用许可证数量增加一个。 如果任何线程试图获得许可证,则选择一个并获得刚刚发布的许可证。 该线程被(重新)启用以进行线程调度。
没有要求发布许可证的线程必须通过致电
acquire()
获得该许可证。 通过应用程序中的编程约定来建立信号量的正确使用。
-
acquire
public void acquire(int permits) throws InterruptedException
从此信号量获取给定数量的许可,阻塞直到所有可用,或者线程为interrupted 。获取给定数量的许可证(如果有),并立即返回,减少给定数量的可用许可证数量。 此方法与循环
for (int i = 0; i < permits; ++i) acquire();
具有相同的效果,只是它以原子方式一次性获取许可:如果没有足够的许可证,那么当前线程将被禁用以进行线程调度,并且在发生以下两种情况之一之前处于休眠状态:
- 其他一些线程为此信号量调用
release
方法之一,然后为当前线程分配许可证,并且可用许可证的数量满足此请求; 要么 - 其他一些线程interrupts当前线程。
如果当前线程:
- 在进入此方法时设置其中断状态; 要么
- 在等待许可时是interrupted ,
InterruptedException
并清除当前线程的中断状态。 将分配给该线程的任何许可证分配给试图获取许可证的其他线程,就像通过调用release()
获得许可一样。- 参数
-
permits
- 获得许可的数量 - 异常
-
InterruptedException
- 如果当前线程被中断 -
IllegalArgumentException
- 如果permits
为负数
- 其他一些线程为此信号量调用
-
acquireUninterruptibly
public void acquireUninterruptibly(int permits)
从此信号量获取给定数量的许可,阻塞直到所有许可都可用。获取给定数量的许可证(如果有),并立即返回,减少给定数量的可用许可证数量。 此方法与循环
for (int i = 0; i < permits; ++i) acquireUninterruptibly();
具有相同的效果,除了它以原子方式一次性获取所有许可证:如果没有足够的许可证可用,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,直到某个其他线程调用此信号量的
release
方法之一并且当前线程接下来被分配许可证并且可用许可证的数量满足此请求。如果当前线程在等待许可时为interrupted ,则它将继续等待并且其在队列中的位置不受影响。 当线程从该方法返回时,将设置其中断状态。
- 参数
-
permits
- 获得许可证的数量 - 异常
-
IllegalArgumentException
- 如果permits
是负数
-
tryAcquire
public boolean tryAcquire(int permits)
只有在调用时所有许可都可用时,才从此信号量获取给定数量的许可。获取给定数量的许可证(如果可用),并立即返回,值为
true
,减少给定金额的可用许可证数量。如果没有足够的许可证,则此方法将立即返回值
false
并且可用许可证的数量不变。即使已将此信号量设置为使用公平订购策略,对
tryAcquire
的调用也会立即获得许可,如果有可用,无论其他线程当前是否在等待。 这种“闯入”行为在某些情况下可能有用,即使它违反了公平性。 如果你想尊重公平性设置,那么使用几乎相同的tryAcquire(permits, 0, TimeUnit.SECONDS)
(它也会检测到中断)。- 参数
-
permits
- 获得许可的数量 - 结果
-
true
如果被收购许可证和false
,否则 - 异常
-
IllegalArgumentException
- 如果permits
为负数
-
tryAcquire
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException
如果在给定的等待时间内所有许可都可用且当前线程不是interrupted ,则从此信号量获取给定数量的许可。获取给定数量的许可证(如果可用)并立即返回,值为
true
,减少给定金额的可用许可证数量。如果没有足够的许可证,那么当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:
- 其他一些线程为此信号量调用
release
方法之一,然后为当前线程分配许可证,并且可用许可证的数量满足此请求; 要么 - 其他一些线程interrupts当前线程; 要么
- 指定的等待时间过去了。
如果获得许可,则返回值
true
。如果当前线程:
- 在进入此方法时设置其中断状态; 要么
- 在等待获得许可证时是interrupted ,
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
。- 结果
-
true
如果此信号量的公平性设置为真
-
hasQueuedThreads
public final boolean hasQueuedThreads()
查询是否有任何线程正在等待获取。 请注意,由于取消可能随时发生,因此true
返回不保证任何其他线程将获得。 该方法主要用于监视系统状态。- 结果
-
true
如果可能有其他线程等待获取锁定
-
getQueueLength
public final int getQueueLength()
返回等待获取的线程数的估计值。 该值只是一个估计值,因为当此方法遍历内部数据结构时,线程数可能会动态更改。 此方法设计用于监视系统状态,而不是用于同步控制。- 结果
- 等待此锁定的估计线程数
-
getQueuedThreads
protected Collection<Thread> getQueuedThreads()
返回包含可能正在等待获取的线程的集合。 因为实际的线程集可能在构造此结果时动态更改,所以返回的集合仅是尽力而为的估计。 返回集合的元素没有特定的顺序。 该方法旨在便于构建提供更广泛监控设施的子类。- 结果
- 线程集合
-
-