假设我有以下代码:
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的最佳解决方案是什么?
发布于 2017-10-06 09:53:54
最简单的方法是只使用signal()而不是signalAll,为添加的每个资源调用signal()一次。这确保每个发出信号的线程都有一个资源可供使用。
这使得其他线程等待到发出信号或超时。没有办法恢复现有的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());https://stackoverflow.com/questions/46601787
复制相似问题