两种方法之间的锁使一种方法挨饿
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
的优先级),您应该使锁 n
和 l
公平。 m
的公平性并不重要。
在我的程序中有两种方法:
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
的优先级),您应该使锁 n
和 l
公平。 m
的公平性并不重要。