2011-02-03 24 views
2

我在下面的代碼死鎖情況:升級Java的讀鎖寫鎖緩存在地圖

private static final ReadWriteLock opClassesLock = new ReentrantReadWriteLock(); 
private static final Map<Class<?>, ServiceClass> opClasses = new WeakHashMap<Class<?>, ServiceClass>(); 
public static ServiceClass get(Class<?> myClass) { 
    opClassesLock.readLock().lock(); 
    try { 
     ServiceClass op = opClasses.get(myClass); 
     if (op == null) { 
      opClassesLock.writeLock().lock(); // deadlock here 
      try { 
       op = new ServiceClass(myClass); 
       opClasses.put(myClass, op); 
      } finally { 
       opClassesLock.writeLock().unlock(); 
      } 
     } 
     return op; 
    } finally { 
     opClassesLock.readLock().unlock(); 
    } 
} 

如果我檢查了文檔ReentrantReadWriteLock,我可以預言這一點:

重入也允許將 從寫入鎖定降級到讀取鎖定,通過 獲取寫入鎖定,然後讀取鎖定然後釋放寫入鎖定 。 但是,從讀鎖 升級到寫鎖不是 可能。

而且只用一個鎖,而不是讀/寫鎖(這不會允許併發讀取),是否有任何其他的方式來解決這樣的問題?

回答

3

Guava使用以及測試溶液類似

new MapMaker().weakKeys().makeMap(); 

。你甚至可以做像

new MapMaker().weakKeys() 
.concurrencyLevel(16) 
.expireAfterAccess(5, TimeUnit.MINUTES) 
.maximumSize(1000) 
.makeComputingMap(new Function<Class<?>, ServiceClass>() { 
    @Override 
    public ServiceClass apply(Class<?> myClass) { 
     return new ServiceClass(myClass); 
    } 
}); 

這應該解決你的整個問題,並提供了很多可能性來調整緩存。


死鎖的原因是在兩個線程持有讀鎖的同時獲取寫鎖。與降級鎖定不同,升級可能會阻止。你需要首先釋放讀鎖。


當你得到寫入鎖定時,你應該測試另一個線程是否還沒有完成這項工作。

+0

是的,那正是我需要的。我將使用它並查看源代碼以瞭解其工作原理。 – 2011-02-03 19:34:52

0

我現在認識到,上面的例子,如果它的工作,將不會是有效的,無論如何,因爲這可能發生

  • 線程1也沒有在地圖上沒有的條目,並獲取寫鎖
  • 線程2看到有在地圖上沒有的條目,並得到寫鎖(但必須等待)
  • 線程1添加條目,並釋放寫鎖
  • 線程2現在增加的條目,但它已經存在

所以有兩種選擇:

  1. 如果輸入不能進行兩次(因副作用)使用的整個方法的單一鎖。或者,釋放讀取鎖定,獲取寫入鎖定,並在計算之前再次檢查條目是否仍然丟失。

  2. 如果該條目可能被創建兩次(如果它只是一個緩存),在獲得寫入鎖定之前釋放讀取鎖定。

0

我認爲在獲取寫入之前釋放讀鎖可能會起作用。 但是在釋放讀鎖之後的確切時刻,其他一些線程可能會將鎖取走,導致前一個線程的等待。