Module java.base

Class Semaphore

java.lang.Object
java.util.concurrent.Semaphore
所有已实现的接口:
Serializable

public class Semaphore extends Object implements Serializable
计数信号量。在概念上,信号量维护一组许可。每个acquire()在必要时阻塞,直到有许可证可用,然后获取它。每个release()添加一个许可证,可能释放一个阻塞的获取者。但是,实际上没有使用许可对象;信号量只是保持可用数量的计数,并相应地采取行动。

信号量通常用于限制可以访问某个(物理或逻辑)资源的线程数量。例如,这里是一个使用信号量来控制对项目池访问的类:

 
 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();
   }

   // 不是特别高效的数据结构;仅供演示

   protected Object[] items = ...; // 正在管理的各种项目
   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; // 不会到达
   }

   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()的线程可以在等待的线程队列中被分配一个许可 - 逻辑上,新线程将自己放在等待线程队列的队首。当公平性设置为true时,信号量保证调用任何acquire方法的线程按照它们调用这些方法的顺序被选中获取许可(先进先出;FIFO)。请注意,FIFO排序必然适用于这些方法内部执行的特定点。因此,一个线程可能在另一个线程之前调用acquire,但在达到排序点之后,以及从方法返回时也是如此。还请注意,未定时的tryAcquire方法不遵守公平性设置,但会获取任何可用的许可。

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

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

内存一致性效果:在调用“release”方法(如release())之前的线程中的操作发生在另一个线程中成功调用“acquire”方法(如acquire())之后的操作。

自从:
1.5
参见:
  • Constructor Summary

    Constructors
    Constructor
    Description
    Semaphore(int permits)
    使用给定数量的许可证和非公平公平性设置创建一个Semaphore
    Semaphore(int permits, boolean fair)
    使用给定数量的许可证和给定公平性设置创建一个Semaphore
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    从此信号量获取一个许可证,阻塞直到有一个可用,或线程被中断
    void
    acquire(int permits)
    从此信号量获取给定数量的许可证,阻塞直到所有许可证都可用,或线程被中断
    void
    从此信号量获取一个许可证,阻塞直到有一个可用。
    void
    acquireUninterruptibly(int permits)
    从此信号量获取给定数量的许可证,阻塞直到所有许可证都可用。
    int
    返回此信号量中当前可用的许可证数量。
    int
    获取并返回所有立即可用的许可证,或者如果有负许可证可用,则释放它们。
    protected Collection<Thread>
    返回一个包含可能正在等待获取许可证的线程的集合。
    final int
    返回等待获取许可证的线程数的估计值。
    final boolean
    查询是否有任何线程正在等待获取许可证。
    boolean
    isFair()
    如果此信号量设置为公平,则返回true
    protected void
    reducePermits(int reduction)
    将可用许可证数量减少指定的减少量。
    void
    释放一个许可证,将其返回给信号量。
    void
    release(int permits)
    释放给定数量的许可证,将它们返回给信号量。
    返回标识此信号量及其状态的字符串。
    boolean
    从此信号量获取一个许可证,仅当调用时有一个可用。
    boolean
    tryAcquire(int permits)
    从此信号量获取给定数量的许可证,仅当调用时所有许可证都可用。
    boolean
    tryAcquire(int permits, long timeout, TimeUnit unit)
    从此信号量获取给定数量的许可证,如果在给定等待时间内所有许可证都可用,并且当前线程未被中断
    boolean
    tryAcquire(long timeout, TimeUnit unit)
    从此信号量获取一个许可证,如果在给定等待时间内有一个可用,并且当前线程未被中断

    Methods declared in class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
  • Constructor Details

    • Semaphore

      public Semaphore(int permits)
      使用给定数量的许可证和非公平公平性设置创建一个Semaphore
      参数:
      permits - 可用的初始许可证数量。此值可能为负数,在这种情况下,必须在授予任何许可证之前发生释放。
    • Semaphore

      public Semaphore(int permits, boolean fair)
      使用给定数量的许可证和给定公平性设置创建一个Semaphore
      参数:
      permits - 可用的初始许可证数量。此值可能为负数,在这种情况下,必须在授予任何许可证之前发生释放。
      fair - 如果此信号量将在争用时保证按照先进先出授予许可证,则为true,否则为false
  • Method Details

    • acquire

      public void acquire() throws InterruptedException
      从此信号量获取一个许可证,阻塞直到有一个可用,或线程被中断

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

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

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

      如果当前线程:

      • 在进入此方法时设置了中断状态;或
      • 在等待许可证时被中断
      那么将抛出InterruptedException,并清除当前线程的中断状态。
      抛出:
      InterruptedException - 如果当前线程被中断
    • acquireUninterruptibly

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

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

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

      如果当前线程在等待许可证时被中断,则它将继续等待,但是分配许可证的时间可能与没有发生中断时将收到许可证的时间不同。当线程从此方法返回时,其中断状态将被设置。

    • tryAcquire

      public boolean tryAcquire()
      从此信号量获取许可,仅在调用时有许可可用时才获取。

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

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

      即使此信号量已设置为使用公平排序策略,调用tryAcquire()将立即获取一个许可,如果有可用的许可,无论其他线程当前是否在等待。这种“插队”行为在某些情况下可能很有用,尽管它破坏了公平性。如果要遵守公平设置,则使用tryAcquire(0, TimeUnit.SECONDS),它几乎等效(还会检测中断)。

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

      public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
      从此信号量获取许可,如果在给定的等待时间内有许可可用且当前线程尚未被中断

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

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

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

      如果获取到许可则返回值true

      如果当前线程:

      • 在进入此方法时已设置中断状态;或
      • 在等待获取许可时被中断
      则抛出InterruptedException,并清除当前线程的中断状态。

      如果指定的等待时间过去,则返回值false。如果时间小于或等于零,则该方法将根本不等待。

      参数:
      timeout - 等待许可的最长时间
      unit - timeout参数的时间单位
      返回:
      如果获取到许可则返回true,如果等待时间过去而未获取到许可则返回false
      抛出:
      InterruptedException - 如果当前线程被中断
    • release

      public void release()
      释放一个许可,将其返回给信号量。

      释放一个许可,将可用许可数增加一个。如果有线程正在尝试获取许可,则会选择一个线程并给予刚释放的许可。该线程将被(重新)启用以进行线程调度。

      没有要求释放许可的线程必须通过调用acquire()来获取该许可。信号量的正确使用由应用程序中的编程约定确定。

    • acquire

      public void acquire(int permits) throws InterruptedException
      从此信号量获取给定数量的许可,阻塞直到所有许可可用,或线程被中断

      获取给定数量的许可,如果它们可用,则立即返回,将可用许可数减少给定数量。此方法的效果与循环for (int i = 0; i < permits; ++i) acquire();相同,只是它一次性获取所有许可:

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

      • 其他线程调用此信号量的一个release方法,并且当前线程将被分配许可,可用许可数满足此请求;或
      • 其他线程中断当前线程。

      如果当前线程:

      • 在进入此方法时已设置中断状态;或
      • 在等待获取许可时被中断
      则抛出InterruptedException,并清除当前线程的中断状态。应分配给此线程的任何许可将分配给其他尝试获取许可的线程,就好像通过调用release()来提供了许可。
      参数:
      permits - 要获取的许可数量
      抛出:
      InterruptedException - 如果当前线程被中断
      IllegalArgumentException - 如果permits为负数
    • acquireUninterruptibly

      public void acquireUninterruptibly(int permits)
      从此信号量获取给定数量的许可,阻塞直到所有许可可用。

      获取给定数量的许可,如果它们可用,则立即返回,将可用许可数减少给定数量。此方法的效果与循环for (int i = 0; i < permits; ++i) acquireUninterruptibly();相同,只是它一次性获取所有许可:

      如果可用许可不足,则当前线程将因线程调度目的而被禁用,并处于休眠状态,直到其他线程调用此信号量的一个release方法,并且当前线程将被分配许可,可用许可数满足此请求。

      如果当前线程在等待许可时被中断,则它将继续等待,其在队列中的位置不受影响。当线程从此方法返回时,其中断状态将被设置。

      参数:
      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
      从此信号量获取给定数量的许可,如果在给定的等待时间内所有许可都可用且当前线程尚未被中断

      获取给定数量的许可,如果它们可用则立即返回,返回值为true,将可用许可数减少给定数量。

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

      • 其他线程调用此信号量的一个release方法,并且当前线程将被分配许可,可用许可数满足此请求;或
      • 其他线程中断当前线程;或
      • 指定的等待时间过去。

      如果获取到许可则返回值true

      如果当前线程:

      • 在进入此方法时已设置中断状态;或
      • 在等待获取许可时被中断
      则抛出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
      返回:
      一个标识此信号量及其状态的字符串