2012-11-02 79 views
0

有許多線程這在同一張地圖,每一個代碼上的元素工作,例如:的Java:這相同的元素上工作鎖定功能

THREAD_1 works on an element with code "1234" 
THREAD_2 works on an element with code "1234" 
THREAD_3 works on an element with code "9876" 
etc... 

元素不是永久的,即THREAD_1可能會刪除元素「1234」,然後再次插入。我想要的是,雖然THREAD_1正在處理元素「1234」(也將其刪除),但THREAD_2必須等待。

有沒有辦法做到這一點?

一種可能的解決方案可能是在一個HashMap中插入一個僞造元件,再與該元件上的「同步」子句強制的同步。你有什麼想法? (顯然是假的元素會保留在還如果一個線程移除了相關的代碼元素地圖)......

回答

2

鑑於您的特殊問題,沒有一個java標準對象可以解決您的所有問題。下面是我認爲是正確的,不留任何不必要的鍵或值在鎖定的地圖解決方案:

// we don't use a ConcurrentHashMap, because we have some other operations 
// that need to be performed in atomically with map.put and map.remove. 
// ConcurrentHashMap would of course also work, but it doesn't remove the 
// need for external synchronization in in our case. 
Map<String, CountingLock> locksMap = new HashMap<String, CountingLock>(); 
... 

HttpResponse myFunction(String key) { 

    CountingLock lock; 
    synchronized(locksMap){ 
     lock = locksMap.get(key); 
     if(lock == null){ 
      lock = new CountingLock(); 
      locksMap.put(key, lock); 
     } 
     lock.prepare(); // has to be done while holding the lock of locksMap. 
         // basically tells other threads that the current 
         // thread intends to acquire the lock soon. This way, 
         // the other threads know not to remove this lock 
         // from locksMap as long as another one has indicated 
         // that he is going to need it soon. 
    } 

    lock.lock(); // has to be done while NOT holding the lock of locksMap, 
       // or we risk deadlock situations. 

    try { 
     // ... 
     // work 
     // ... 
    } finally { 
     synchronized(locksMap) { 
      if(lock.unlock() == 0){ 
       // no other thread is intending to use this lock any more. 
       // It is safe to remove it from the map. The next thread 
       // will just have to recreate a new lock for the same key. 
       locksMap.remove(key); 
      } 
     } 
    } 

    return SOMETHING;  
} 

private static class CountingLock { 
    // The number of threads that are trying to access the protected Key 
    private AtomicInteger interestedThreads = new AtomicInteger(0); 

    private Lock lock = new ReentrantLock(); 

    public void prepare(){ 
     interestedThreads.incrementAndGet(); 
    } 

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

    public int unlock(){ 
     lock.unlock(); 
     return interestedThreads.decrementAndGet();    
    } 
} 

此代碼應該按預期在所有情況下。這是一個有趣的問題來解決:-)

+1

該解決方案應該可以工作。在整個地圖上,同步條款不太好,這可能是一個瓶頸。不過謝謝,我認爲迄今爲止這是最好的解決方案......奇怪的是沒有命名的鎖...... – Massimo

+0

它不應該是一個瓶頸太大,因爲鎖內的所有操作都非常快。由於你顯然正在使用一些HTTP相關的東西,我懷疑你在「工作」塊中所做的任何事情都要像執行同步塊的內容一樣多。因此,這些同步塊應該不會對性能產生任何可衡量的影響。 – LordOfThePigs

+0

我同意你的意見。它工作(顯然),它似乎不是一個大瓶頸;)謝謝你! – Massimo

0

您應該使用ConcurrentHashMap

的哈希表支持檢索的完全併發和期望可調整併發更新。

檢索操作(包括get)通常不會阻止,所以可以與更新操作交迭(包括put和remove)。

更新操作中允許的併發由可選的concurrencyLevel構造器參數(默認16),其被用作用於內部施膠的提示引導。該表在內部分區以嘗試允許指定數量的併發更新而不存在爭用。由於放置在哈希表基本上是隨機的,實際的併發性會有所不同

+1

是的,我用它。但問題是:每個元素都有一個關聯的計數器,並且可以將這些元素存儲在永久性存儲器(例如文件)中。我需要確定的是,如果有人正在處理該元素(可以在內存中或文件中),則任何人都無法訪問它。然後,我需要一個通用的方法... – Massimo

+0

@Massimo使用一個類,它將包含'AutomicBoolean'的地圖中的值將被標記,如果有人正在處理它,那麼你將不會刪除它,也不會返回值。 –

+1

那你說的是我的解決方案?問題是:我怎樣才能避免這張地圖無限增長?有人應該刪除無用的元素... – Massimo

1

我們使用我們稱之爲LockMap的事情。

一個LockMap基本上是:

Map<Object, ReadWriteLock> 

我們有一個同步的方法來獲取特定對象的鎖。

由於Maps依賴於等價而不是身份,所以兩個對象equal()都會給您相同的鎖定。

所以:

lock1 = map.get(new Thing(1)); 
lock2 = map.get(new Thing(1)); 

lock1 == lock2 = true 

這是很方便。

一旦你獲得鎖定,你可以鎖定它,因爲你想控制對對象的訪問。

我們要做的另一件事是使用LRU映射(使用LinkedHashMap - 請參閱this),以便舊對象鍵可以在未使用時脫落。

相關問題