簡體   English   中英

JAVA-並發和鎖

[英]JAVA - Concurrency and locks

我需要創建一個像這樣的哈希圖:

Map<String, List<Integer>>

我正在使用2個線程。

這是輸出映射的示例:

---------------------
| xxx | a  b  c  d  |
| yyy | a' b' c' d' |
| zzz | A  B  C  D  |
---------------------

這兩個線程都有助於創建條目。 因此,例如,線程1具有要存儲的以下數據集:

-----------------------------------
| 1 |  (xxx,a)  (yyy,a')  (zzz,A) |
| 2 |  (xxx,b)  (yyy,b')  (zzz,B) |
-----------------------------------

線程2具有:


| 1 |  (xxx,c)  (yyy,c')  (zzz,C) |
| 2 |  (xxx,d)  (yyy,d')  (zzz,D) |
-----------------------------------

因此,同時,兩個線程可以計算相同的密鑰:線程1(xxx,a)和線程2(xxx,c)。

我可以使用的最佳鎖定/解鎖機制是什么?

我嘗試了以下

ReadWriteLock lock = new ReentrantReadWriteLock();

executor.submit(() -> {
   lock.writeLock().lock();
   try {
      sleep(1);
      map.put("foo", "bar");
  } finally {
     lock.writeLock().unlock();
  }

});

但我不想鎖定整個哈希圖。 我想創建一個鎖IF,並且僅在處理期間兩個線程應該在同一個鍵上工作時才創建。

謝謝

首先,您應該使用並發Map:

Map<String, List<Integer>> map = new ConcurrentHashMap<>();

其次, 被多個線程訪問之前,應使用同步列表填充它,如下所示:

for (String key : keys) {
    map.put(key, Collections.synchronizedList(new ArrayList<>()));
}

或者您可以像這樣及時地進行操作:

List<Integer> list = map.get(key);
if (list == null) {
    map.putIfAbsent(key, Collections.synchronizedList(new ArrayList<>()));
    list = map.get(key);
}

而已。 現在一切都應該是線程安全的。

注意:重要的是不要在從地圖中獲取列表之前使用列表。 由於並發,一個以上的線程可能會嘗試將丟失的列表添加到映射中,但是只有其中一個會成功。 因此,嘗試添加丟失的列表后,請始終從地圖中獲取該列表。

不確定其他答案是否會滿足您的要求,因為您需要從地圖上進行讀取(以查看鍵是否已存在),然后以原子操作進行更新。

不知道這是否是最優雅的解決方案,但是您可以使用類似的方法...

導入java.util.HashSet; 導入java.util.Map; 導入java.util.Set; 導入java.util.concurrent.ConcurrentHashMap;

public class KeyLockMap<K, V> {

    private Map<K, V> map;

    private Set<K> lockKeys;

    public KeyLockMap() {
        map = new ConcurrentHashMap<K, V>();
        lockKeys = new HashSet<K>();
    }

    public void lock(K key) throws InterruptedException {
        synchronized(lockKeys) {
            // Keep waiting while the lockKeys set contains the key we want to update
            while(lockKeys.contains(key)) {
                lockKeys.wait();
            }
        }
    }

    public void unlock(K key) {
        synchronized(lockKeys) {
            lockKeys.remove(key);
            // Notify any threads that may be waiting for this key to be removed
            lockKeys.notifyAll();
        }
    }

    public Map<K, V> getMap() {
        return map;
    }
}

現在在您的線程中,您可以執行...

keyLockMap.lock(KEY);
Map<String, List<Integer>> map = keyLockMap.getMap();
...do updates...
keyLockMap.unlock(KEY);

希望這可以幫助。


另一種可行的方法是在ConcurrentHashMap上使用“ putIfAbsent”,例如...

List<Integer> newValue = new CopyOnWriteArrayList<>();
List<Integer> value = concurrentMap.putIfAbsent(KEY, newValue);

if(value == null) {
    value = newValue;
}

value.add(DATA);

如果另一個線程首先執行putIfAbsent ,則將獲得該線程創建的CopyOnWriteArrayList ,並且可以向其中添加數據,因為CopyOnWriteArrayList是線程安全的。

(已修改為考慮到在首次認沽權時返回空值的原因...)

無需重新設計輪子, java.util.concurrent包已經包含Map接口的線程安全實現-ConcurrentHashMap

但是,您還必須同步您的列表(請參見下面的BretC注釋)。 因此,最方便的解決方案可能是使用Guava同步多圖 像那樣:

public static void main(String[] args) {

    Multimap <String, Integer> map = Multimaps.synchronizedMultimap(HashMultimap.<String, Integer> create());

    // In Thread 1
    map.put("foo", 1);

    //In Thread 2
    map.put("foo", 2);

    System.out.println(map); // prints {foo=[1, 2]}
}

您應該使用Hashtable而不是Hashmap。 Hashtable是線程安全的(已經同步),不需要再次實現鎖定/解鎖機制。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM