2014-10-05 93 views
0

我有以下代碼:Condition實例signalAll()不返回

@Log4j 
public class ItemStore { 
    final Lock lock = new ReentrantLock(); 
    final Condition hasItem = lock.newCondition(); 

    private Map<String, String> store = new Hashtable<>(); 

    public void put(String handle, String item) { 
     store.put(handle, item); 
     log.info("stored " + handle); 
     hasItem.signalAll(); 
     log.info("signaled all threads"); 
    } 

    public String fetchWithTimeout(String handle, long timeoutInSec) throws InterruptedException { 
     try { 
      lock.lock(); 
      while (!store.containsKey(handle)) { 
       log.info("store doesn't have " + handle + "; keep waiting"); 
       hasItem.await(timeoutInSec, TimeUnit.SECONDS); 
      } 
      return store.get(handle); 
     } finally { 
      lock.unlock(); 
     } 
    } 
} 



@Test 
public void test_withPut() throws InterruptedException { 
    ItemStore itemStore = new ItemStore(); 
    final String key = "foo"; 
    final String value = "bar"; 

    new Thread() { 
     @Override 
     public void run() { 
      try { 
       Thread.sleep(3000); 
       log.info("slept 3 seconds"); 
       itemStore.put(key, value); 
      } catch (Exception e) { 
      } 
     } 
    }.start(); 

    log.info("fetching"); 
    String actual = itemStore.fetchWithTimeout(key, 20); 
    log.info("actual = " + actual); 
    assertEquals(actual, value); 
} 

基於從如下測試日誌:

2014-10-05 17:52:48 INFO com.tns.ct.downloader.tests.commons.ItemStoreTest.test_withPut():36 - fetching 
2014-10-05 17:52:48 INFO com.tns.ct.downloader.tests.commons.ItemStore.fetchWithTimeout():30 - store doesn't have foo; keep waiting 
2014-10-05 17:52:51 INFO com.tns.ct.downloader.tests.commons.ItemStoreTest.run():29 - slept 3 seconds 
2014-10-05 17:52:51 INFO com.tns.ct.downloader.tests.commons.ItemStore.put():21 - stored foo 
2014-10-05 17:53:08 INFO com.tns.ct.downloader.tests.commons.ItemStoreTest.test_withPut():38 - actual = bar 

似乎hasItem.signalAll()已經一去不返,因爲signaled all threads日誌從未發佈過。另一個線索是程序僅在達到20秒超時時退出。那麼,爲什麼在這種情況下signalAll()方法被阻止?

+0

你的代碼有些奇怪。您在匿名Thread子類中使用'itemStore'而不使用變量爲final。檢查是否沒有使用類中的任何'ItemStore'實例。 – mp911de 2014-10-05 18:23:31

+0

@ mp911de:他可能正在使用Java 8,其中final不再是必需的。該變量只能是有效的最終變量,編譯器會檢測它是否並非全部。 – 2014-10-05 18:35:52

+0

與這個特定的問題無關,推薦的習慣用法是立即* try-finally塊之外的鎖。 – 2014-10-08 20:31:22

回答

2

報價從documentation of signalAll()

可以(並且通常確實)要求當前線程保持了與此條件有關的鎖時,調用此方法的實現。從documentation of ReentrantLock.newCondition()

報價:

返回的Condition實例支持相同的用途爲做當用於對象監視器方法(等待,通知和notifyAll)內置監視器鎖定。

如果在調用任何Condition等待或信號傳遞方法時未鎖定此鎖定,則會拋出IllegalMonitorStateException。

不知道爲什麼一個IllegalMonitorException沒有在您的測試拋出,但什麼是肯定是推杆線程時,它調用的條件signalAll()不持有鎖。

編輯:@Fildor提到,可能會拋出一個異常,但被測試中的空catch塊吞噬。不要使用空的catch塊。如果你拋出一個運行時異常來包裝捕獲的異常而不是吞下它,問題就會變得明顯。

+0

期望的異常是否被空的catch塊吞噬? – Fildor 2014-10-05 18:20:59

+2

@Fildor:當然,是的。我沒有注意到它。另一個證據表明空的抓塊是邪惡的。 – 2014-10-05 18:29:08

+0

_ @ JBNizet:我修復了我的代碼。感謝您的回答。另外,我確認在我的原始代碼中確實發生了IllegalMonitorStateException異常。解決方法是簡單地使用典型的'try {lock.lock(); // do something} finally {lock.unlock()}'來封裝put方法體,這是一個典型的鎖定實踐。 – JBT 2014-10-05 19:28:47