Java - 信号量中的优先级

Java - Priority in semaphore

我有多个线程访问外部资源——浏览器。但是一次只有一个线程可以访问它。所以,我使用信号量来同步它们。但是,一个从 GUI 获取输入然后访问浏览器获取结果的线程应该优先于其他线程,我不确定如何使用信号量来实现它。

我在想,每个线程在获取到信号量后,都会检查队列中是否有优先线程在等待,如果有,就释放它,重新等待。只有优先级线程获取后不释放

这是一个很好的解决方案吗?或者 Java API 中还有什么我可以使用的吗?

您在这里混淆了概念。

信号量 只是"synchronize" 线程交互的众多选项之一。它们与线程优先级和线程调度没有任何关系

另一方面,线程优先级本身就是一个主题。你有办法 Java 影响他们;但此类行动的结果在很大程度上取决于潜在的 platform/OS;和 JVM 实现本身。理论上,使用这些优先级是 easy, but as said; reality is more complicated.

换句话说:你只能使用你的信号量来确保在一个时间点只有一个线程在使用你的队列。当 CPU 周期成为问题时,它根本无助于确保您的 GUI 读取线程胜过其他线程。但如果你幸运的话,你的问题的答案将是简单地调用 setPriority();使用不同的优先级。

Java 中没有同步原语可让您以您想要的方式将一个线程优先于其他线程。

但是您可以使用另一种方法来解决您的问题。不要同步线程,而是让它们生成小任务(例如,Runnable 对象)并将这些任务放入 PriorityBlockingQueue 中,其中来自 GUI 线程的任务具有最高优先级。单个工作线程将从该队列中轮询任务并执行它们。这将保证互斥和优先排序。

ThreadPoolExecutor 中有接受阻塞队列的特殊构造函数。因此,您所需要的只是这样一个执行器,它具有随 PriorityBlockingQueue<Runnable> 提供的单个线程。然后将你的任务提交给这个执行器,它会处理剩下的事情。

如果您决定选择这种方法,您可能会对 post 感兴趣:How to implement PriorityBlockingQueue with ThreadPoolExecutor and custom tasks

这是一个简单、没有多余装饰的答案。这类似于 read/write 锁的工作方式,除了每个储物柜都有独占访问权(通常所有读者并行进行)。请注意,它使用Semaphore,因为这几乎总是错误的使用结构。

public class PrioLock {
  private boolean _locked;
  private boolean _priorityWaiting;

  public synchronized void lock() throws InterruptedException {
    while(_locked || _priorityWaiting) {
      wait();
    }
    _locked = true;
  }

  public synchronized void lockPriority() throws InterruptedException {
    _priorityWaiting = true;
    try {
      while(_locked) {
          wait();
      }
      _locked = true;
    } finally {
      _priorityWaiting = false;
    }
  }

  public synchronized void unlock() {
    _locked = false;
    notifyAll();
  }
}

您可以像 java.util.concurrent 中的锁类型之一一样使用它:

普通线程:

_prioLock.lock();
try {
  // ... use resource here ...
} finally {
  _prioLock.unlock();
}

"Priority" 主题:

_prioLock.lockPriority();
try {
  // ... use resource here ...
} finally {
  _prioLock.unlock();
}

更新:

回复关于 "preemptive" 线程交互的评论:

一般意义上,你不能那样做。您可以构建自定义功能,将 "pause points" 添加到锁定部分,这将允许低优先级线程屈服于高优先级线程,但这会充满危险。

您唯一可以实际做的就是中断工作线程使其退出锁定的代码块(假设您的工作代码响应中断)。这将允许高优先级线程以低优先级线程丢失正在进行的工作为代价更快地进行(并且您可能还必须实现回滚逻辑)。

为了实现这一点,您需要:

  1. 锁定成功时记录"current thread"
  2. lockPriority()中,如果找到
  3. ,则中断"current thread"
  4. 实现 lock()/unlock()(低优先级)调用之间的逻辑,以便:
    1. 它在合理的时间范围内响应中断
    2. 它在中断时执行任何必要的 "rollback" 代码
  5. 可能实现 "retry" 逻辑 lock()/unlock()(低优先级)调用之外,以便重新完成任何丢失的工作被打断时