|
|||||||||
摘要: 嵌套 | 字段 | 构造方法 | 方法 | 详细信息: 字段 | 构造方法 | 方法 |
java.util.concurrent
类 Semaphore
java.lang.Object java.util.concurrent.Semaphore
- 所有已实现的接口:
- Serializable
-
public class Semaphore
- extends Object
- implements Serializable
一个计数信号量。从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个 acquire()
,然后再获取该许可。每个 release()
添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。例如,下面的类使用信号量控制对内容池的访问:
class Pool { private static final 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
多个许可。小心,在未将公平设置为 true 时使用这些方法会增加不确定延期的风险。
- 从以下版本开始:
- 1.5
- 另请参见:
- 序列化表格
构造方法摘要 | |
---|---|
Semaphore(int permits) 用给定的许可数和非公平的公平设置创建一个 Semaphore。 |
|
Semaphore(int permits, boolean fair) 用给定的许可数和给定的公平设置创建一个 Semaphore。 |
方法摘要 | |
---|---|
void |
acquire() 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被 中断 。 |
void |
acquire(int permits) 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,否则线程是 已中断的 。 |
void |
acquireUninterruptibly() 从此信号量中获取许可,在有可用的许可前将其阻塞。 |
void |
acquireUninterruptibly(int permits) 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。 |
int |
availablePermits() 返回此信号量中当前可用的许可数。 |
int |
drainPermits() 获取并返回立即可用的所有许可。 |
protected Collection<Thread> |
getQueuedThreads() 返回一个 collection,包含可能等待获取的线程。 |
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) 如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被 中断 ,则从此信号量获取给定数目的许可。 |
boolean |
tryAcquire(long timeout, TimeUnit unit) 如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被 中断 ,则从此信号量获取一个许可。 |
从类 java.lang.Object 继承的方法 |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
构造方法详细信息 |
---|
Semaphore
public Semaphore(int permits)
-
用给定的许可数和非公平的公平设置创建一个 Semaphore。
- 参数:
-
permits
- 初始的可用许可数目。此值可能为负数,在这种情况下,必须在授予任何获取前进行释放。
Semaphore
public Semaphore(int permits, boolean fair)
-
用给定的许可数和给定的公平设置创建一个 Semaphore。
- 参数:
-
permits
- 初始的可用许可数目。此值可能为负数,在这种情况下,必须在授予任何获取前进行释放。 -
fair
- 如果此信号量保证在争用时按先进先出的顺序授予许可,则为 ture,否则为 false。
方法详细信息 |
---|
acquire
public void acquire() throws InterruptedException
-
从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被
中断
。获取一个许可(如果提供了一个)并立即返回,将可用的许可数减 1。
如果没有可用的许可,则在发生以下两种情况之一前,禁止将当前线程用于线程安排目的并使其处于休眠状态:
如果当前线程:
- 被此方法将其已中断状态设置为 on ;或者
- 在等待许可时被
中断
。
InterruptedException
,并且清除当前线程的已中断状态。 -
-
- 抛出:
-
InterruptedException
- 如果当前线程是已中断的。 - 另请参见:
-
Thread.interrupt()
acquireUninterruptibly
public void acquireUninterruptibly()
-
从此信号量中获取许可,在有可用的许可前将其阻塞。
获取一个许可(如果提供了一个)并立即返回,将可用的允许数减 1。
如果没有可用的许可,则在其他某些线程调用此信号量的
release()
方法,并且当前线程是下一个要被分配许可的线程前,禁止当前线程用于线程安排目的并使其处于休眠状态。如果当前线程在等待许可时是
已中断的
,那么它将继续等待,但是与没有发生中断,其将接收允许的时间相比,为该线程分配许可的时间可能改变。当线程确实从此方法返回后,将设置其中断状态。 -
-
tryAcquire
public boolean tryAcquire()
-
仅在调用时此信号量存在一个可用许可,才从信号量获取许可。
获取一个许可(如果提供了一个)并立即返回,其值为 true,将可用的许可数减 1。
如果没有可用的许可,则此方法立即返回并且值为 false.
即使已将此信号量设置为使用公平排序策略,但是调用 tryAcquire() 也将 立即获取许可(如果有一个可用),而不管当前是否有正在等待的线程。在某些情况下,此“闯入”行为可能很有用,即使它会打破公平性也如此。如果希望遵守公平设置,则使用
tryAcquire(0, TimeUnit.SECONDS)
,它几乎是等效的(它也检测中断)。 -
-
- 返回:
- 如果获取了许可,则返回 true;否则返回 false。
tryAcquire
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
-
如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被
中断
,则从此信号量获取一个许可。获取一个许可(如果提供了一个)并立即返回,其值为 true,将可用的许可数减 1。
如果没有可用的允许,则在发生以下三种情况之一前,禁止将当前线程用于线程安排目的并使其处于休眠状态:
如果获取了许可,则返回值为 true。
如果当前线程:
- 被此方法将其已中断状态设置为 on ;或者
- 在等待获取许可的同时被
中断
。
InterruptedException
,并且清除当前线程的已中断状态。如果超出了指定的等待时间,则返回值为 false。如果该时间小于或等于 0,则方法根本不等待。
-
-
- 参数:
-
timeout
- 等待许可的最多时间。 -
unit
- timeout 参数的时间单位。 - 返回:
- 如果获取了许可,则返回 true;如果获取许可前超出了等待时间,则返回 false。
- 抛出:
-
InterruptedException
- 如果当前线程是已中断的。 - 另请参见:
-
Thread.interrupt()
release
public void release()
-
释放一个许可,将其返回给信号量。
释放一个许可,将可用的许可数增加 1。如果任意线程试图获取许可,则选中一个线程并将刚刚释放的许可给予它。然后针对线程安排目的启用(或再启用)该线程。
不要求释放许可的线程必须通过调用
acquire()
来获取许可。通过应用程序中的编程约定来建立信号量的正确用法。 -
-
acquire
public void acquire(int permits) throws InterruptedException
-
从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,否则线程是
已中断的
。获取给定数目的许可(如果提供了)并立即返回,将可用的许可数减去给定的量。
如果没有足够的可用许可,则在发生以下两种情况之一前,禁止将当前线程用于线程安排目的并使其处于休眠状态:
如果当前线程:
- 被此方法将其已中断状态设置为 on ;或者
- 在等待许可时被
中断
。
InterruptedException
,并且清除当前线程的已中断状态。任何原本应该分配给此线程的许可将被分配给其他试图获取许可的线程,就好像已通过调用release()
而使许可可用一样。 -
-
- 参数:
-
permits
- 要获取的许可数。 - 抛出:
-
InterruptedException
- 如果当前线程是已中断的。 -
IllegalArgumentException
- 如果许可小于 0。 - 另请参见:
-
Thread.interrupt()