2010-09-21 67 views
0

我已經閱讀了幾個與此類似的問題,但沒有一個答案提供瞭如何在保持鎖完整性的同時清理內存的想法。我估計在給定時間鍵值對的數量是成千上萬,但是在數據結構的整個生命週期中鍵值對的數量實際上是無限的(實際上它可能不會更多超過十億,但我正在編碼到最壞的情況)。鎖定緩存鍵

我有一個接口:

public interface KeyLock<K extends Comparable<? super K>> { 

    public void lock(K key); 

    public void unock(K key); 

} 

有一個默認的實現:

public class DefaultKeyLock<K extends Comparable<? super K>> implements KeyLock<K> { 

    private final ConcurrentMap<K, Mutex> lockMap; 

    public DefaultKeyLock() { 
    lockMap = new ConcurrentSkipListMap<K, Mutex>(); 
    } 

    @Override 
    public void lock(K key) { 
    Mutex mutex = new Mutex(); 
    Mutex existingMutex = lockMap.putIfAbsent(key, mutex); 
    if (existingMutex != null) { 
     mutex = existingMutex; 
    } 
    mutex.lock(); 
    } 

    @Override 
    public void unock(K key) { 
    Mutex mutex = lockMap.get(key); 
    mutex.unlock(); 
    } 

} 

這工作得很好,但地圖上永遠不會被清理。我至今一個乾淨的實現是:

public class CleanKeyLock<K extends Comparable<? super K>> implements KeyLock<K> { 

    private final ConcurrentMap<K, LockWrapper> lockMap; 

    public CleanKeyLock() { 
    lockMap = new ConcurrentSkipListMap<K, LockWrapper>(); 
    } 

    @Override 
    public void lock(K key) { 
    LockWrapper wrapper = new LockWrapper(key); 
    wrapper.addReference(); 
    LockWrapper existingWrapper = lockMap.putIfAbsent(key, wrapper); 
    if (existingWrapper != null) { 
     wrapper = existingWrapper; 
     wrapper.addReference(); 
    } 

    wrapper.addReference(); 
    wrapper.lock(); 
    } 

    @Override 
    public void unock(K key) { 
    LockWrapper wrapper = lockMap.get(key); 
    if (wrapper != null) { 
     wrapper.unlock(); 
     wrapper.removeReference(); 
    } 
    } 

    private class LockWrapper { 

    private final K key; 

    private final ReentrantLock lock; 

    private int referenceCount; 

    public LockWrapper(K key) { 
     this.key = key; 
     lock = new ReentrantLock(); 
     referenceCount = 0; 
    } 

    public synchronized void addReference() { 
     lockMap.put(key, this); 
     referenceCount++; 
    } 

    public synchronized void removeReference() { 
     referenceCount--; 
     if (referenceCount == 0) { 
     lockMap.remove(key); 
     } 
    } 

    public void lock() { 
     lock.lock(); 
    } 

    public void unlock() { 
     lock.unlock(); 
    } 
    } 

} 

這適用於兩個線程訪問單個按鍵鎖定功能,但一旦第三線程引入了鎖完整性不再保證。有任何想法嗎?

+0

忽略多餘addReference()在鎖定方法調用。我在嘗試一些想法,並忘記在發佈代碼時將它們帶出。 – user453385 2010-09-21 02:14:09

回答

0

我不買,這適用於兩個線程。試想一下:

  • (線程A)調用lock(x)的,現在持有鎖X
  • 線程切換
  • (線程B)調用鎖(X)的putIfAbsent()返回x的當前包裝
  • 線程切換
  • (線程A)調用解鎖(X),則包裝器引用計數擊中0和它得到從地圖中刪除
  • (線程A)調用鎖(X),的putIfAbsent()插入一個新包裝爲x
  • (線程A)在新的包裝鎖
  • 線程切換
  • (線程B)上的舊包裝鎖

如何:

  • LockWrapper爲1的引用計數開始
  • 如果引用計數爲0,則addReference()返回false。
  • in lock(),如果existingWrapper!= null,則對其調用addReference()。如果返回false,它已被從地圖上刪除,因此,我們回送,並從的putIfAbsent()
+0

您在一般情況下是正確的。我喜歡重新嘗試putIfAbsent的想法,特別是因爲我可以通過比較和交換操作來實現它。 – user453385 2010-09-22 13:05:42

0

我會用一定的排列默認的條紋鎖定再試一次,因爲你可以它的大小到您期望的併發級別。雖然可能會有散列衝突,但一個好的散佈器可以解決這個問題。如果這些鎖用於較短的關鍵部分,那麼您可能會在ConcurrentHashMap中創建爭用,導致優化失敗。

歡迎您適應我的實現,儘管我只實現了動態版本以獲得樂趣。它在實踐中似乎沒有用處,所以只有固定用於生產。您可以使用ConcurrentHashMap中的hash()函數來提供良好的傳播。

ReentrantStripedLock中, http://code.google.com/p/concurrentlinkedhashmap/wiki/IndexableCache