创建一个模仿信号量的 class 但许可的数量不应超过 0

Creating a class that mimics a semaphore but the number of permits should never exceed 0

我遇到了使用信号量设计队列的问题,这样所有获取信号量的线程都必须等待某个线程释放它们。但这里的问题是,如果在没有线程等待时调用 release,那么它不应该像真正的信号量那样产生任何影响,在真正的信号量中将添加额外的许可。我开始尝试这样的事情:

public class QueueOfThreads {
    
    private Semaphore valve = new Semaphore(0);
    volatile int count = 0;
    
    
    public void acquire() throws InterruptedException {
        synchronized(this) {
            count++;
        }
        valve.acquire();
    }
    
    public void release() {
        synchronized(this) {
            if(count > 0) {
                valve.release();
                count--;
            }
            else {
                System.out.println("will not release since no thread is waiting");
            }
        }
    }

}

但我可以看出这是错误的,因为如果线程在 count++ 之后被抢占,则可以在获取之前调用释放。

我花了很多时间试图找到一种方法来确保在任何发布之前至少调用一次获取。但是我总是遇到同样的问题,在获取信号量后我无法向其他线程发出获取信号量的信号,因为当前线程将处于等待状态。但是如果我在获取信号量之前发出信号,那么线程可以在实际获取信号量之前被抢占。

请告诉我是否可以像这样写 class 以及如何写?

我遇到这个问题是因为 Allen B. Downey 在一本名为“The Little Book of Semaphores”的书中提到:

》信号量也可以用来表示一个队列,这种情况下,初始值为0,通常代码会写成除非有线程在等待,否则无法发出信号,所以该值信号量永远不会是正数。"

您可以利用 Object.notify() 释放一个正在等待的线程,如果有的话:

public class QueueOfThreads {

  public synchronized void acquire() throws InterruptedException {
    wait();
  }

  public synchronized void release() {
    notify();
  }
}

但是,这仅适用于 JVM,不会出现虚假唤醒。 如果虚假唤醒会发生,那么实现会更复杂:

public class QueueOfThreads {
  int threadCount = 0;
  boolean notified = false;

  public synchronized void acquire() throws InterruptedException {
    threadCount++;
    do {
      wait();
    } while (!notified);
    threadCount--;
    notified = false;
  }

  public synchronized void release() {
    if (threadCount==0) {
      return;
    }
    notified = true;
    notify();
  }
}