Java锁定条件

时间:2017-10-06 08:45:04

标签: java multithreading concurrency

假设我有以下代码:

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的等待(不重新开始)?

1 个答案:

答案 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());