两种方法之间的锁使一种方法挨饿

Locks between two methods starving one method

在我的程序中有两种方法:

public void methodA() { //gets called very often
   //write something to file
}

public void methodB() {
  //write something to file
}

methodA 经常被客户调用,而 methodB 只是偶尔被调用。但是,我需要确保无论何时客户端想要调用 methodB 它都可以这样做(在 methodA 的当前执行可能完成之后)。我试图在每个方法中引入一个带有锁定对象的同步块,但是 methodB 似乎饿死了,因为 methodA 被更频繁地调用。 我该如何解决这个问题?

听起来您需要 公平 Lock。要制作其中之一,您应该将 true 作为参数传递给构造函数。

Lock lock = new ReentrantLock(true);

public void methodA() {
    lock.lock();
    try {
        // Write something to a file.
    } finally {
        lock.unlock();
    }
}

public void methodB() {
    lock.lock();
    try {
        // Write something to a file.
    } finally {
        lock.unlock();
    }
}

ReentrantLock 有一个 constructor 带有公平参数,可以防止你的情况出现饥饿。

我建议使用优先队列。简单地说,两个队列,一个用于方法 A,另一个用于方法 B。另一个线程在队列上工作的逻辑如下:当B的队列不为空时,操作它,否则,为A做队列。

您可以为此使用 semaphores。它基本上适用于将字段设置为某个值的想法..可以说已锁定。而在另一种方法上,你做了一段时间......无限重复,直到另一个过程完成。您可以通过在不同的线程中调用方法来使用信号量..并使用关键字 synchronized void..

信号量部分是硬件部分是软件解决方案。也有纯软件解决方案出来。例如 Peterson's algorithm

如果您想使方法 B 优先于方法 A,这是我能想到的最简单的方法:

private Object writeLock = new Object();
private Object highPriorityLock = new Object();
private int highPriorityLockReleaseCount = 0;
private int highPriorityLockLockCount = 0;

public void methodA() {
    synchronized (writeLock) {
        synchronized (highPriorityLock) {
            // Wait until there are no more highPriorityLocks
            while (highPriorityLockLockCount != highPriorityLockReleaseCount) {
                highPriorityLock.wait();
            }
        }
        // Do write (thread holds write lock)
    }
}

public void methodB() {
    synchronized (highPriorityLock) {
        // Get current lock count
        int lockCount = highPriorityLockLockCount;
        // Increment lock count by one
        highPriorityLockLockCount++;
        // Wait until lock is acquired (current release count reaches saved lock count)
        while (lockCount != highPriorityLockReleaseCount) {
            highPriorityLock.wait();
        }
    }
    synchronized (writeLock) {
        // Do write (thread holds write lock)
    }
    synchronized (highPriorityLock) {
        // Relase high priority lock by incrementing current counter
        highPriorityLockReleaseCount++;
        highPriorityLock.notifyAll();
    }
}

一定要处理异常并确保始终正确释放高优先级锁

如果您主要关心的是 methodB 调用不会被 阻塞 或饥饿,您可以通过 非阻塞来解决并发问题I/O 操作.

Java NIO.2 java.nio.channels.AsynchronousFileChannel can provide for such needs. You can find a good usage explanation and example here.

这与this question大致相同。该问题评分最高的答案给出了三个选项。选项 2 和 3 都假定 methodB 将始终从同一个线程调用,您没有说是这种情况,但选项 1 应该有效。移植到 Java 的选项 1 是:

private final Lock m = new ReentrantLock();
private final Lock n = new ReentrantLock();
private final Lock l = new ReentrantLock();

public void methodA() {
    l.lock();
    n.lock();
    m.lock();
    n.unlock();
    try {
        doA();
    } finally {
        m.unlock();
        l.unlock();
    }
}

public void methodB() {
    n.lock();
    m.lock();
    n.unlock();
    try {
        doB();
    } finally {
        m.unlock();
    }
}

这使方法 B 绝对优先于方法 A,这与 OldCurmudgeon 的答案不同,后者给予两者同等的优先级。如果您还希望算法公平(除了 methodB 优于 methodA 的优先级),您应该使锁 nl 公平。 m的公平性并不重要。