模块 java.base

类 Semaphore

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 的信号量,并且使用它时最多只有一个许可可用,可以用作互斥锁。这通常称为 binary semaphore ,因为它只有两种状态:一种可用许可,或零种可用许可。当以这种方式使用时,二进制信号量具有属性(与许多 Lock 实现不同),即“锁”可以由所有者以外的线程释放(因为信号量没有所有权的概念)。这在某些专门的上下文中很有用,例如死锁恢复。

此类的构造函数可选择接受一个 fairness 参数。当设置为 false 时,此类不保证线程获取许可的顺序。特别是,barging 是允许的,也就是说,调用 acquire() 的线程可以在一直在等待的线程之前分配一个许可 - 逻辑上新线程将自己置于等待线程队列的头部。当 fairness 设置为 true 时,信号量保证调用任何 acquire 方法的线程被选择以按照它们对这些方法的调用被处理的顺序(先进先出;FIFO)获得许可。请注意,FIFO 排序必然适用于这些方法中的特定内部执行点。因此,一个线程有可能在另一个线程之前调用 acquire,但在另一个线程之后到达排序点,从方法返回时也是如此。另请注意,未计时的 tryAcquire 方法不遵守公平设置,但会采用任何可用的许可。

通常,用于控制资源访问的信号量应该被初始化为公平的,以确保没有线程因访问资源而饿死。当使用信号量进行其他类型的同步控制时,非公平排序的吞吐量优势通常超过公平性考虑。

此类还为 acquire release 一次提供多个许可提供便利方法。这些方法通常比循环更有效。但是,它们没有建立任何优先顺序。例如,如果线程 A 调用 s.acquire(3 ) 并且线程 B 调用 s.acquire(2) ,并且两个许可变为可用,则不能保证线程 B 将获得它们,除非它的获取先到并且信号量 s 处于公平模式。

内存一致性影响:调用“释放”方法之前线程中的操作,例如 release() 发生在之前 在另一个线程中成功“获取”方法(例如 acquire())之后的操作。

自从:
1.5
参见:
  • 构造方法总结

    构造方法
    构造方法
    描述
    Semaphore(int permits)
    创建具有给定数量的许可和非公平公平设置的 Semaphore
    Semaphore(int permits, boolean fair)
    使用给定的许可数量和给定的公平设置创建一个 Semaphore
  • 方法总结

    修饰符和类型
    方法
    描述
    void
    从此信号量获取许可,阻塞直到一个可用,或者线程为 interrupted
    void
    acquire(int permits)
    从此信号量获取给定数量的许可,阻塞直到所有许可都可用,或者线程为 interrupted
    void
    从此信号量获得许可,阻塞直到一个可用。
    void
    acquireUninterruptibly(int permits)
    从此信号量获取给定数量的许可,阻塞直到所有许可可用。
    int
    返回此信号量中可用的当前许可数。
    int
    获取并归还所有立即可用的许可,或者如果存在负面许可,则释放它们。
    protected Collection<Thread>
    返回一个集合,其中包含可能正在等待获取的线程。
    final int
    返回等待获取的线程数的估计值。
    final boolean
    查询是否有任何线程正在等待获取。
    boolean
    如果此信号量的公平性设置为真,则返回 true
    protected void
    reducePermits(int reduction)
    按指示的减少量减少可用许可的数量。
    void
    释放许可,将其返回给信号量。
    void
    release(int permits)
    释放给定数量的许可,将它们返回给信号量。
    返回标识此信号量及其状态的字符串。
    boolean
    从该信号量获取许可,前提是在调用时许可可用。
    boolean
    tryAcquire(int permits)
    仅当调用时所有许可都可用时,才从此信号量获取给定数量的许可。
    boolean
    tryAcquire(int permits, long timeout, TimeUnit unit)
    如果在给定的等待时间内所有许可都可用并且当前线程尚未 interrupted ,则从该信号量获取给定数量的许可。
    boolean
    tryAcquire(long timeout, TimeUnit unit)
    如果一个在给定的等待时间内变得可用并且当前线程尚未 interrupted ,则从该信号量获取许可。

    在类 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 - true 如果此信号量将保证竞争中许可的先进先出授予,否则 false
  • 方法详情

    • acquire

      public void acquire() throws InterruptedException
      从此信号量获取许可,阻塞直到一个可用,或者线程为 interrupted

      获得许可证,如果有可用许可证并立即返回,则可用许可证的数量减少一个。

      如果没有可用的许可,则当前线程将出于线程调度目的而被禁用并处于休眠状态,直到发生以下两种情况之一:

      • 一些其他线程为此信号量调用 release() 方法,当前线程接下来将被分配一个许可;或者
      • 一些其他线程 中断 当前线程。

      如果当前线程:

      • 在进入此方法时设置其中断状态;或者
      • 在等待许可证时是interrupted
      然后抛出InterruptedException 并清除当前线程的中断状态。
      抛出:
      InterruptedException - 如果当前线程被中断
    • acquireUninterruptibly

      public void acquireUninterruptibly()
      从此信号量获得许可,阻塞直到一个可用。

      获得许可证,如果有可用许可证并立即返回,则可用许可证的数量减少一个。

      如果没有可用的许可,则当前线程出于线程调度目的而被禁用并处于休眠状态,直到某个其他线程为此信号量调用 release() 方法并且当前线程接下来被分配许可。

      如果当前线程在等待许可时是interrupted,那么它会继续等待,但是线程被分配许可的时间可能会与它在没有中断发生的情况下收到许可的时间相比发生变化。当线程确实从此方法返回时,将设置其中断状态。

    • tryAcquire

      public boolean tryAcquire()
      从该信号量获取许可,前提是在调用时许可可用。

      获取一个许可证,如果一个可用并立即返回,其值为 true ,将可用许可证的数量减少一个。

      如果没有可用的许可,则此方法将立即返回值 false

      即使此信号量已设置为使用公平排序策略,调用 tryAcquire() will 也会立即获得可用许可,无论其他线程当前是否正在等待。这种“闯入”行为在某些情况下可能很有用,即使它破坏了公平性。如果你想遵守公平设置,那么使用几乎等效的tryAcquire(0, TimeUnit.SECONDS) (它也检测到中断)。

      返回:
      true 如果获得许可,false 否则
    • tryAcquire

      public boolean tryAcquire(long timeout, TimeUnit  unit) throws InterruptedException
      如果一个在给定的等待时间内变得可用并且当前线程尚未 interrupted ,则从该信号量获取许可。

      获取一个许可证,如果一个可用并立即返回,其值为 true ,将可用许可证的数量减少一个。

      如果没有可用的许可,则当前线程将出于线程调度目的而被禁用并处于休眠状态,直到发生以下三种情况之一:

      • 一些其他线程为此信号量调用 release() 方法,当前线程接下来将被分配一个许可;或者
      • 一些其他线程中断当前线程;或者
      • 指定的等待时间已过。

      如果获得许可,则返回值 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 方法之一,当前线程接下来将被分配许可,并且可用许可的数量满足此请求;或者
      • 一些其他线程 中断 当前线程。

      如果当前线程:

      • 在进入此方法时设置其中断状态;或者
      • 在等待许可证时是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 will 的调用也会立即获得许可(如果许可可用),无论其他线程当前是否正在等待。这种“闯入”行为在某些情况下可能很有用,即使它破坏了公平性。如果你想遵守公平设置,那么使用几乎等效的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 方法之一,当前线程接下来将被分配许可,并且可用许可的数量满足此请求;或者
      • 一些其他线程中断当前线程;或者
      • 指定的等待时间已过。

      如果获得许可,则返回值 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()
      返回一个集合,其中包含可能正在等待获取的线程。因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。返回集合的元素没有特定的顺序。此方法旨在促进提供更广泛的监视功能的子类的构造。
      返回:
      线程的集合
    • toString

      public String  toString()
      返回标识此信号量及其状态的字符串。括号中的状态包括字符串 "Permits =",后跟许可数。
      重写:
      toString 在类 Object
      返回:
      标识此信号量及其状态的字符串