- java.lang.Object
-
- java.util.concurrent.Semaphore
-
- All Implemented Interfaces:
-
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()
时,不会保持同步锁定,因为这将阻止某个项目返回到池中。 信号量封装了限制对池的访问所需的同步,与保持池本身一致性所需的任何同步分开。信号量被初始化为一个,并且被使用,使得它只有至多一个允许可用,可以用作互斥锁。 这通常被称为二进制信号量 ,因为它只有两个状态:一个许可证可用,或零个允许可用。 当以这种方式使用时,二进制信号量具有属性(与许多
Lock
实现不同),“锁”可以由除所有者之外的线程释放(因为信号量没有所有权概念)。 这在某些专门的上下文中是有用的,例如死锁恢复。此类的构造函数可选择接受公平参数。 当设置为false时,此类不会保证线程获取许可的顺序。 特别是允许进行驳斥 ,也就是说,调用
acquire()
的线程可以在一直等待的线程之前分配一个许可证 - 逻辑上新的线程将自身置于等待线程队列的头部。 当公平设置为真时,信号量保证调用任何acquire
方法的线程被选择以按照它们调用这些方法的顺序获得许可(先进先出; FIFO)。 请注意,FIFO排序必须适用于这些方法中的特定内部执行点。 因此,一个线程可以在另一个线程之前调用acquire
,但是到另一个线程之后的顺序点,并且类似地从方法返回。 另请注意,未定义的tryAcquire
方法不符合公平性设置,但将采取任何可用的许可证。通常,用于控制资源访问的信号量应该被公平地初始化,以确保线程没有被访问资源。 当使用信号量进行其他类型的同步控制时,非正常排序的吞吐量优势往往超过公平性。
本课程同时提供了
acquire
和release
多个许可证的便利方法。 这些方法通常比循环更有效和有效。 但是,他们没有建立任何偏好顺序。 例如,如果线程A调用s.acquire(3
),并且线程B调用s.acquire(2)
,并且两个许可证变得可用,则不保证线程B将获得它们,除非它的获取是第一,并且信号量s
处于公平模式。内存一致性效果:在另一个线程中成功执行“获取”方法(例如
acquire()
之前,调用“释放”方法之前的线程中的操作,如release()
happen-before 。- 从以下版本开始:
- 1.5
- 另请参见:
- Serialized Form
-
-
方法摘要
所有方法 接口方法 具体的方法 Modifier and Type 方法 描述 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 ,则从该信号量获取许可。
-
-
-
构造方法详细信息
-
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(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
如果这个信号量具有公平性
-
hasQueuedThreads
public final boolean hasQueuedThreads()
查询任何线程是否等待获取。 请注意,因为取消可能会在任何时候发生,所以true
返回不能保证任何其他线程将获得。 该方法主要用于监视系统状态。- 结果
-
true
如果可能有其他线程等待获取锁
-
getQueueLength
public final int getQueueLength()
返回等待获取的线程数的估计。 该值只是一个估计,因为线程数可能会在此方法遍历内部数据结构时动态更改。 该方法设计用于监控系统状态,不用于同步控制。- 结果
- 估计等待这个锁的线程数
-
getQueuedThreads
protected Collection<Thread> getQueuedThreads()
返回一个包含可能正在等待获取的线程的集合。 因为在构建此结果时,实际的线程集可能会动态更改,所以返回的集合只是尽力而为的估计。 返回的集合的元素没有特定的顺序。 该方法旨在便于构建提供更广泛监控设施的子类。- 结果
- 线程的收集
-
-