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()
时,不会保持同步锁定,因为这将阻止某个项目返回到池中。 信号量封装了限制对池的访问所需的同步,与保持池本身一致性所需的任何同步分开。
信号量被初始化为一个,并且被使用,使得它只有至多一个允许可用,可以用作互斥锁。 这通常被称为二进制信号量 ,因为它只有两个状态:一个许可证可用,或零个许可证可用。 当以这种方式使用时,二进制信号量具有属性(与许多Lock
实现不同),“锁”可以由除所有者之外的线程释放(因为信号量没有所有权概念)。 这在某些专门的上下文中是有用的,例如死锁恢复。
此类的构造函数可选择接受公平参数。 当设置为false时,此类不会保证线程获取许可的顺序。 特别是, 闯入是允许的,也就是说,一个线程调用acquire()
可以提前已经等待线程分配的许可证-在等待线程队列的头部逻辑新的线程将自己。 当公平设置为真时,信号量保证调用acquire
方法的线程被选择以按照它们调用这些方法的顺序获得许可(先进先出; FIFO)。 请注意,FIFO排序必须适用于这些方法中的特定内部执行点。 因此,一个线程可以在另一个线程之前调用acquire
,但是在另一个线程之后到达排序点,并且类似地从方法返回。 另请注意, 未定义的tryAcquire
方法不符合公平性设置,但将采取任何可用的许可证。
通常,用于控制资源访问的信号量应该被公平地初始化,以确保线程没有被访问资源。 当使用信号量进行其他类型的同步控制时,非正常排序的吞吐量优势往往超过公平性。
本课程还提供了方便的方法, 一次acquire
和release
多个许可证。 当没有公平地使用这些方法时,请注意增加无限期延期的风险。
内存一致性效应:在另一个线程中成功执行“获取”方法(如acquire()
之前,调用“释放”方法之前的线程中的操作,例如release()
happen-before 。
Constructor and Description |
---|
Semaphore(int permits)
创建一个
Semaphore 与给定数量的许可证和非公平公平设置。
|
Semaphore(int permits, boolean fair)
创建一个
Semaphore 与给定数量的许可证和给定的公平设置。
|
Modifier and Type | Method and Description |
---|---|
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 。
|
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,则从该信号量获取许可。
|
public Semaphore(int permits)
Semaphore
与给定数量的许可证和非公平公平设置。
permits
- permits
的初始许可证。
该值可能为负数,在这种情况下,必须在任何获取被授予之前发布释放。
public Semaphore(int permits, boolean fair)
Semaphore
与给定数量的许可证和给定的公平设置。
permits
- permits
的初始许可证。
该值可能为负数,在这种情况下,必须在任何获取被授予之前发布释放。
fair
-
true
如果这个信号量将保证首先在竞争中首先授予许可证,否则
false
public void acquire() throws InterruptedException
获得许可证,如果有可用并立即返回,则将可用许可证数量减少一个。
如果没有可用的许可证,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,直至发生两件事情之一:
release()
方法,当前线程旁边将分配一个许可证; 要么 如果当前线程:
InterruptedException
被关上,当前线程的中断状态被清除。
InterruptedException
- 当前线程是否中断
public void acquireUninterruptibly()
获得许可证,如果有可用并立即返回,则将可用许可证数量减少一个。
如果没有许可证可用,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,直到某个其他线程调用此信号量的release()
方法,并且当前线程将被分配一个许可证。
如果当前线程是interrupted等待许可证,那么它将继续等待,但线程被分配许可证的时间可能会比接收到许可证的时间更改,而不会发生中断。 当该线程从该方法返回时,其中断状态将被设置。
public boolean tryAcquire()
获得许可证,如果有可用并立即返回,值为true
,将可用许可证数量减少一个。
如果没有许可证可用,那么该方法将立即返回值为false
。
即使这个信号量被设置为使用一个合理的订购策略,如果一个可用的话,那么呼叫tryAcquire()
将立即获得许可,无论其他线程是否正在等待。 这种“趸船”行为在某些情况下是有用的,尽管它打破了公平。 如果要尊重公平性,请使用tryAcquire(0, TimeUnit.SECONDS)
,几乎相当于(也检测到中断)。
true
如果获得许可证,
false
false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
获得许可证,如果可用并立即返回,值为true
,将可用许可证数量减少一个。
如果没有许可证可用,那么当前的线程将被禁用以进行线程调度,并且处于休眠状态,直至发生三件事情之一:
release()
方法,当前线程旁边将分配一个许可证; 要么 如果获得许可证,则返回值true
。
如果当前线程:
InterruptedException
被关上,当前线程的中断状态被清除。
如果指定的等待时间过去,则返回值false
。 如果时间小于或等于零,该方法根本不会等待。
timeout
- 等待许可证的最长时间
unit
-
timeout
参数的时间单位
true
如获得许可证,如果获得许可证之前经过的等待时间为
false
InterruptedException
- 当前线程是否中断
public void release()
发放许可证,将可用许可证的数量增加一个。 如果任何线程尝试获取许可证,那么选择一个被授予刚被释放的许可证。 (重新)线程调度用于线程调度。
没有要求发布许可证的线程必须通过调用acquire()
获取该许可证。 信号量的正确使用通过应用程序中的编程惯例来确定。
public void acquire(int permits) throws InterruptedException
获得一定数量的许可证,如果可用,并立即返回,将可用许可证数量减少给定数量。
如果没有足够的许可证可用,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,直到发生两件事情之一:
release
方法,当前线程旁边将分配许可证,并且可用许可证的数量满足此请求; 要么 如果当前线程:
InterruptedException
被关上,当前线程的中断状态被清除。
将分配给此线程的任何许可证分配给尝试获取许可证的其他线程,如同通过拨打release()
可用的许可证一样 。
permits
- 获得许可证的数量
InterruptedException
- 当前线程是否中断
IllegalArgumentException
- 如果
permits
是否定的
public void acquireUninterruptibly(int permits)
获得一定数量的许可证,如果可用,并立即返回,将可用许可证数量减少给定数量。
如果不足的许可证可用,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,直到某个其他线程调用此信号量的一个release
方法,当前线程将被分配许可证,并且可用许可证的数量满足此请求。
如果当前线程在等待许可时为interrupted ,则它将继续等待,并且其在队列中的位置不受影响。 当该线程从该方法返回时,其中断状态将被设置。
permits
- 获得许可证的数量
IllegalArgumentException
- 如果
permits
为负数
public boolean tryAcquire(int permits)
获得给定数量的许可证(如果有),并立即返回,值为true
,将可用许可证数量减少给定金额。
如果许可证不足,则该方法将立即返回值为false
,可用许可证数量不变。
即使这个信号量被设置为使用公平的订购策略,如果一个可用的话,呼叫tryAcquire
将立即获得许可证,无论其他线程是否正在等待。 这种“趸船”行为在某些情况下是有用的,尽管它打破了公平。 如果要尊重公平性,请使用tryAcquire(permits, 0, TimeUnit.SECONDS)
,这几乎相当(也检测到中断)。
permits
- 获得许可证的数量
true
如果获得许可证,
false
false
IllegalArgumentException
- 如果
permits
为负数
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException
获得指定数量的许可证,如果它们可用并立即返回,值为true
,将可用许可证数量减少给定数量。
如果没有足够的许可证可用,那么当前的线程将被禁用以进行线程调度,并且处于休眠状态,直至发生三件事情之一:
release
方法,当前线程旁边将分配许可证,可用许可证数量满足此请求; 要么 如果获得许可证,则返回值true
。
如果当前线程:
InterruptedException
被关上,当前线程的中断状态被清除。
这将被分配给该线程的任何许可证,而不是被分配给其他线程试图获取许可,仿佛已经取得了许可证可通过调用release()
。
如果指定的等待时间过去,则返回值false
。 如果时间小于或等于零,该方法根本不会等待。 这将被分配给该线程的任何许可证,而不是被分配给其他线程试图获取许可,仿佛已经取得了许可证可通过调用release()
。
permits
- 获得许可证的数量
timeout
- 等待许可证的最长时间
unit
-
timeout
参数的时间单位
true
如果获得所有许可证和
false
如果在所有许可证获得之前经过的等待时间
InterruptedException
- 当前线程是否中断
IllegalArgumentException
- 如果
permits
是否定的
public void release(int permits)
释放给定数量的许可证,增加可用许可证的数量。 如果任何线程尝试获取许可证,则选择一个线程并给出刚刚发布的许可证。 如果可用许可证的数量满足该线程的请求,则该线程(线程)被重新启用以进行线程调度; 否则线程将等待,直到有足够的许可证可用。 如果在该线程的请求已经满足之后还有可用的许可证,那么这些许可证依次被分配给其他尝试获取许可证的线程。
没有要求释放许可证的线程必须通过调用acquire
获取该许可证。 信号量的正确使用通过应用程序中的编程惯例来确定。
permits
- 发放许可证的数量
IllegalArgumentException
- 如果
permits
是否定的
public int availablePermits()
该方法通常用于调试和测试。
public int drainPermits()
protected void reducePermits(int reduction)
acquire
的不同之处在于它不阻止等待许可证变为可用。
reduction
- 要移除的许可证数量
IllegalArgumentException
- 如果
reduction
为负数
public boolean isFair()
true
。
true
如果这个信号量具有公平性
public final boolean hasQueuedThreads()
true
返回不保证任何其他线程将获得。
该方法主要用于监视系统状态。
true
如果可能有其他线程等待获取锁
public final int getQueueLength()
protected Collection<Thread> getQueuedThreads()