假设我有以下代码:
private final ReentrantLock resourcesLock = new ReentrantLock(true);
private Condition resourcePresentCondition= resourcesLock.newCondition();
public void requestRes() throws InterruptedException {
resourcesLock.lock();
try {
if(resources.isEmpty()) {
if(!resourcePresentCondition.await(Config.STARVE_TIME_SECONDS, TimeUnit.SECONDS)) {
if(resources.isEmpty()) {
return;
}
}
}
//No resources left if other threads gather them first
Resource resource = resources.removeFirst();
} finally {
resourcesLock.unlock();
}
}
现在,多个线程进入requestRes()方法,如果没有资源,它们都会等待条件。另一种方法生成资源并调用resourcePresentCondition.signalAll()
。在此之后,如果时间没有用完(或者如果时间已经用完并且存在资源 - 只是为了避免一切都在同一时间发生),就会消耗资源。
问题在于有时资源被清空,获得resourcePresentCondition信号的线程抛出异常,因为资源列表为空(.removeFirst()异常)。
什么是最好的解决方案来避免这种情况并使线程恢复resourcePresentCondition的等待(不重新开始)?
答案 0 :(得分:2)
最简单的方法是仅使用signal()
代替signalAll
,为每个添加的资源调用signal()
一次。这可以确保发出信号的每个线程都保证有1个资源可供使用。
这使得其他线程等到发出信号或发生超时。没有办法恢复现有的await()
,并且您不想开始编写自定义逻辑来跟踪等待的时间。
由于您正在使用公平锁定(new ReentrantLock(true);
)(在这种情况下应该如此),因此发出所有线程的信号甚至没有意义。您不希望只有资源的消费者竞争另一个资源。
一种简化事情的不同方法是使用公平Semaphore
。
// Consumer
private final Semaphore semaphore = new Semaphore(0, true);
public void requestRes() throws InterruptedException {
if(!semaphore.tryAcquire(Config.STARVE_TIME_SECONDS, TimeUnit.SECONDS))
return; // No resource available, and timed out
Resource resource = resources.removeFirst();
}
// Producer, giving out as many semaphores as resources produced
semaphore.release(resources.size());