【问题标题】:Prioritization on ReentrantLock's ConditionReentrantLock 条件的优先级
【发布时间】:2021-08-27 19:34:02
【问题描述】:

问题:我有一组Threads,其中一些必须优先于其他获得ReentrantLock

解决方案:我可以想象有一个 公平 ReentrantLock 和 2 个 Condition 队列:lowPriorityhighPriority。重点是highPrioritylowPriority 之前发出信号。考虑到ReentrantLock 的公平性,Threads 在highPriority 中被阻止总是在Threads 在lowPriority 中被阻止。

实施:

public class Main {
    public static final Lock lock = new ReentrantLock(true);
    public static final Condition lowPriority = lock.newCondition();
    public static final Condition highPriority = lock.newCondition();
    public static boolean cond;

    public static void lowPriority() throws InterruptedException {
        try {
            lock.lock();
            while(!cond) {
                lowPriority.await();
            }
            cond = false;
            System.out.println("low");
        } finally {
            lock.unlock();
        }
    }

    public static void highPriority() throws InterruptedException {
        try {
            lock.lock();
            while(!cond) {
                highPriority.await();
            }
            cond = false;
            System.out.println("high");
        } finally {
            lock.unlock();
        }
    }

    public static void setCond(){
        try{
            lock.lock();
            cond = true;
            highPriority.signalAll();
            lowPriority.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

问题: 让我对解决方案感到困惑的问题是,我无法从 JMM 的角度正式证明,一旦 Threads 被阻止在 @ 987654336@ 他们总是赢 Threads 被 lowPriority 屏蔽。

当然,我进行了一些实验,高优先级的线程总是获胜,但形式上正确吗?

【问题讨论】:

  • 我认为,这只是由 ReentrantLock 实现管理,而不是 JMM。但是,当您尝试从 JMM 的角度进行推理时,分享您迄今为止的想法会很有帮助。
  • @Holger 我最困惑的是当signalAll 返回时,假设所有Threads 等待给定Condifition 已经在ReentrantLock 中排队是可靠的吗? s WaitSet?
  • 我验证了当前实现确实将所有线程转移到等待队列中,并且在启用公平性时,发出信号的线程检查它们是否是队列中的第一个。但是,我不确定我们是否假设实现必须按照规范的措辞那样做。
  • 也许您可以使用PriorityQueue 来保存带有Condition 的对象。仅当线程需要等待时,才将项目添加到队列中。任务完成后,您轮询队列并向其他人发出信号。
  • @Holger 我重新思考了这个想法,发现了以下竞争条件:Thread 1 表示所有在Condition highPriority 上阻塞的高优先级线程。 Thread 2 到达中间并调用setCond()highPrioritylowPriority 线程带来重要更新。出于公平考虑,Thread 2 在所有highPriority 线程之后都在ReentrantLock 上排队。然后在lowPriority 上阻塞的所有线程都由Thread 1 发出信号,并在Thread 2.Thread 2 再次发出高优先级和低优先级线程信号之后调度,高优先级线程丢失更新

标签: java multithreading concurrency jvm reentrantlock


【解决方案1】:

据我所知,对于代码

highPriority.signalAll();
lowPriority.signalAll();

不能保证在highPriority 上等待的某些线程会在lowPriority 上等待的任何线程之前唤醒。
即使fairness==true 线程可以按随机顺序唤醒 1:

但是请注意,锁的公平性并不能保证线程调度的公平性。因此,使用公平锁的许多线程之一可能会连续多次获得它,而其他活动线程没有进展并且当前没有持有锁。

此外,lowPriority.signalAll() 将继续唤醒所有低优先级线程,即使在该进程中间出现了新的高优先级线程。

所以我会在 lowPriority 的开头插入额外的逻辑,它检查是否有任何等待的高优先级线程,如果有,让它们先运行。
类似的东西:

public final class Main {

  private final Lock lock = new ReentrantLock();
  private final Condition lowPriority = lock.newCondition();
  private final Condition highPriority = lock.newCondition();
  private int numWaitingHighPriority = 0;
  private boolean cond;

  public void lowPriority() throws InterruptedException {
    lock.lock();
    try {
      while (!cond || (numWaitingHighPriority > 0)) {
        if (numWaitingHighPriority > 0) {
          highPriority.signal();
        }
        lowPriority.await();
      }
      cond = false;
      System.out.println("low");
    } finally {
      lock.unlock();
    }
  }

  public void highPriority() throws InterruptedException {
    lock.lock();
    try {
      numWaitingHighPriority++;
      try {
        while (!cond) {
          highPriority.await();
        }
      } finally {
        numWaitingHighPriority--;
      }
      cond = false;
      System.out.println("high");
    } finally {
      lock.unlock();
    }
  }

  public void setCond() {
    lock.lock();
    try {
      cond = true;
      ((numWaitingHighPriority > 0) ? highPriority : lowPriority).signal();
    } finally {
      lock.unlock();
    }
  }
}

【讨论】:

    猜你喜欢
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 2012-12-09
    • 1970-01-01
    • 1970-01-01
    • 2020-08-22
    • 2011-12-29
    相关资源
    最近更新 更多