2017-10-06 54 views
1

假设我有以下代码:Java的锁定条件

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

我想看看[同步](https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html) – Nico

+0

添加另一测试'resources.isEmpty()'之前'removeFirst'。 – Dimitri

+1

@Nico你可能想看看[高级并发对象](https://docs.oracle.com/javase/tutorial/essential/concurrency/highlevel.html) – Kayaman

回答

1

最简单的方法是使用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()); 
+0

第一种方法(也是我相信的第二种方法)的问题是,有时候signal()太迟了,即使有些生产者生产资源,消费者因为没有及时获得信号而“死亡”。这些情况下的时间戳是相同的。(produce()&“death event”)。 – taviss

+0

_你不希望只有资源的消费者争夺另一个资源_--事实并非如此,因为消费资源后有一个“全职”,但无论如何,“signal()”方式更接近解决方案。 (+公平的锁) – taviss

+0

这样做的一种方式是实际使用超时进行_cheat_,并在资源生成时阻止时间不足。这引发了与以前相同的问题,如果资源被其他线程占用,则无法恢复等待。 – taviss