美文网首页JUC
Semaphore | 译

Semaphore | 译

作者: zlzl_ | 来源:发表于2022-11-02 00:17 被阅读0次

    https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html

    class Semaphore

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

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

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

    此类还提供方便的方法来一次获取和释放多个许可。当这些方法在不公平的情况下使用时,请注意无限期推迟的风险增加。

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

    Method Detail

    public void acquire() throws InterruptedException

    从这个信号量获取一个许可,阻塞直到有一个可用,或者线程被中断。
    获得一个许可证,如果一个可用并立即返回,将可用许可证的数量减少一个。

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

    • 其他一些线程为此信号量调用 release() 方法,然后为当前线程分配一个许可; 或者
    • 其他一些线程中断当前线程。
      如果当前线程:
    • 在进入此方法时设置其中断状态; 或者
    • 在等待许可时被打断,
      然后抛出 InterruptedException 并清除当前线程的中断状态。

    public void acquireUninterruptibly()

    从这个信号量获取一个许可,阻塞直到一个可用。
    获得一个许可证,如果一个可用并立即返回,将可用许可证的数量减少一个。

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

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

    public boolean tryAcquire()

    仅当调用时可用时,才从此信号量获取许可。
    获取一个许可,如果一个可用并立即返回,值为 true,将可用许可的数量减少一个。

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

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

    public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException

    如果在给定的等待时间内可用并且当前线程未被中断,则从此信号量获取许可。
    获取一个许可,如果一个可用并立即返回,值为 true,将可用许可的数量减少一个。

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

    • 其他一些线程为此信号量调用 release() 方法,然后为当前线程分配一个许可;或者
    • 其他一些线程中断当前线程;或者
    • 指定的等待时间已过。
      如果获得许可,则返回值 true。
      如果当前线程:
    • 在进入此方法时设置其中断状态;或者
    • 在等待获得许可时被打断,
      然后抛出 InterruptedException 并清除当前线程的中断状态。
      如果经过指定的等待时间,则返回值 false。如果时间小于或等于零,则该方法根本不会等待。

    public void release()

    释放许可证,将其返回给信号量。
    释放许可证,将可用许可证的数量增加一个。 如果任何线程试图获得许可,则选择一个并给予刚刚释放的许可。 该线程(重新)启用用于线程调度目的。

    不要求释放许可的线程必须通过调用acquire() 获得该许可。 信号量的正确使用是通过应用程序中的编程约定建立的。

    public void acquire(int permits) throws InterruptedException

    从此信号量获取给定数量的许可,阻塞直到所有许可都可用,或者线程被中断。
    获取给定数量的许可(如果可用)并立即返回,将可用许可的数量减少给定的数量。

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

    • 其他一些线程为此信号量调用其中一种释放方法,当前线程接下来被分配许可并且可用许可的数量满足该请求;或者

    • 其他一些线程中断当前线程。
      如果当前线程:

    • 在进入此方法时设置其中断状态;或者

    • 在等待许可时被打断,
      然后抛出 InterruptedException 并清除当前线程的中断状态。任何要分配给该线程的许可都被分配给试图获取许可的其他线程,就好像通过调用 release() 使许可可用一样。

    public void acquireUninterruptibly(int permits)

    从此信号量获取给定数量的许可,阻塞直到所有许可都可用。
    获取给定数量的许可(如果可用)并立即返回,将可用许可的数量减少给定的数量。

    如果可用的许可不足,则当前线程将被禁用以用于线程调度目的并处于休眠状态,直到某个其他线程为此信号量调用释放方法之一,当前线程接下来将被分配许可并且可用许可的数量满足此请求 .

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

    public boolean tryAcquire(int permits)

    仅当调用时所有许可都可用时,才从此信号量获取给定数量的许可。
    获取给定数量的许可(如果可用),并立即返回,值为 true,将可用许可的数量减少给定的数量。

    如果可用的许可不足,则此方法将立即返回 false 值,并且可用许可的数量不变。

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

    public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException

    如果在给定的等待时间内全部可用并且当前线程没有被中断,则从此信号量获取给定数量的许可。
    获取给定数量的许可,如果它们可用并立即返回,值为 true,将可用许可的数量减少给定的数量。

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

    • 其他一些线程为此信号量调用其中一种释放方法,当前线程接下来被分配许可并且可用许可的数量满足该请求;或者
    • 其他一些线程中断当前线程;或者
    • 指定的等待时间已过。
      如果获得许可,则返回值 true。

    如果当前线程:

    • 在进入此方法时设置其中断状态;或者
    • 在等待获得许可时被中断,
      然后抛出 InterruptedException 并清除当前线程的中断状态。任何要分配给该线程的许可,都会被分配给试图获取许可的其他线程,就好像这些许可是通过调用 release() 获得的一样。
      如果经过指定的等待时间,则返回值 false。如果时间小于或等于零,则该方法根本不会等待。任何要分配给该线程的许可,都会被分配给试图获取许可的其他线程,就好像这些许可是通过调用 release() 获得的一样。

    public void release(int permits)

    释放给定数量的许可,将它们返回给信号量。
    释放给定数量的许可,将可用许可的数量增加该数量。 如果任何线程试图获取许可,则选择一个并给予刚刚释放的许可。 如果可用许可的数量满足该线程的请求,则为线程调度目的(重新)启用该线程; 否则线程将等待直到有足够的许可可用。 如果在满足该线程的请求后仍有可用的许可,则这些许可将依次分配给试图获取许可的其他线程。

    不要求释放许可的线程必须通过调用acquire获得该许可。 信号量的正确使用是通过应用程序中的编程约定建立的。

    public int availablePermits()

    返回此信号量中可用的当前许可数。
    此方法通常用于调试和测试目的。

    public int drainPermits()

    获取并返回所有立即可用的许可证。

    protected void reducePermits(int reduction)

    按指示的减少量减少可用许可证的数量。 此方法在使用信号量来跟踪变得不可用的资源的子类中很有用。 此方法与获取的不同之处在于它不会阻止等待许可变为可用。

    public boolean isFair()

    如果此信号量的公平性设置为 true,则返回 true。

    public final boolean hasQueuedThreads()

    查询是否有线程正在等待获取。 请注意,因为取消可能随时发生,所以真正的返回并不能保证任何其他线程将永远获取。 此方法主要设计用于监控系统状态。

    public final int getQueueLength()

    返回等待获取的线程数的估计值。 该值只是一个估计值,因为在此方法遍历内部数据结构时线程数可能会动态变化。 此方法设计用于监控系统状态,而不是用于同步控制。

    protected Collection<Thread> getQueuedThreads()

    返回一个包含可能正在等待获取的线程的集合。 因为在构造这个结果时实际的线程集可能会动态变化,所以返回的集合只是一个尽力而为的估计。 返回集合的元素没有特定的顺序。 此方法旨在促进提供更广泛的监视设施的子类的构建。

    相关文章

      网友评论

        本文标题:Semaphore | 译

        本文链接:https://www.haomeiwen.com/subject/xogdtdtx.html