5

Kodu:Eşzamanlı iş parçacıklarında `(` `` `` `` `` `` `` (`` `` `` `` `` `` `` `işlevini kullanırken HashMap'in" ConcurrentModificationException "ı nasıl önlenir?

Ben put(K,V) arayarak içine K-V çiftini koyacağız bir HashMap

private Map<K, V> map = new HashMap<>(); 

Bir yöntemi var.

diğer yöntem, değerleri rastgele elementlerden oluşan bir dizi elde etmek için ister:

int size = map.size(); // size > 0 
V[] value_array = map.values().toArray(new V[size]); 
Random rand = new Random(); 
int start = rand.nextInt(size); int end = rand.nextInt(size); 
// return value_array[start .. end - 1] 

iki yöntem, iki farklı eşzamanlı iplikleri olarak adlandırılır. Hata


:

Bir ConcurrentModificationException hata var:

at java.util.HashMap$HashIterator.nextEntry(Unknown Source) 
at java.util.HashMap$ValueIterator.next(Unknown Source) 
at java.util.AbstractCollection.toArray(Unknown Source) 

Biri dizisindeki toArray() yöntem aslında HashMap ve diğer dizisindeki bir put() modifikasyon yineleme gibi görünüyor oluşur.

Question: How to avoid "ConcurrentModificationException" while using HashMap.values().toArray() and HashMap.put() in concurrent threads?
Directly avoiding using values().toArray() in the second method is also OK.

+1

bir senkronize bloğunda 'map' erişen kodu yürütün: onu her yerde uygularsanız (' senkronize (harita) {...} ' – Titus

+1

' senkronize (harita) {..} 'çalışması gerekir). Collections.synchronizedMap işe yaramaz. http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedMap%28java.util.Map%29 adresine bakın. – Thilo

cevap

4

için put çağrısı tersi toArray arama yürütülürken bloke edilir ve böylece senkronizasyon bir seviyede sağlamak gerekir.

  1. put aramalarınızı sarın ve (harita kendisi veya başka bir nesne olabilir) aynı kilit nesne üzerinde senkronize toArraysynchronized yılında bloklar: üç iki basit yaklaşım vardır.
  2. Collections.synchronizedMap()

    private Map<K, V> map = Collections.synchronizedMap(new HashMap<>()); 
    

  3. Kullanım yerine HashMap bir ConcurrentHashMap kullanarak senkronize harita haline haritanızı açın

    .

DÜZENLEME: Collections.synchronizedMap kullanarak sorun values() getiri çağrısına kez eşzamanlılık koruma kaybolur olmasıdır. Bu noktada, put() ve toArray() çağrıları eşzamanlı olarak yürütülebilir. ConcurrentHashMap, biraz benzer bir soruna sahiptir, ancak yine de kullanılabilir. ConcurrentHashMap.values() için Dokümanlar:

The view's iterator is a "weakly consistent" iterator that will never throw ConcurrentModificationException , and guarantees to traverse elements as they existed upon construction of the iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.

+0

@Thilo - Right. Üç yaklaşım. :) –

+0

@Thilo Teşekkürler. Ancak ConcurrentHashMap'in mutlaka ConcurrentModificationException'ı çözemediğine dair bazı sözler okudum (ama şimdi kaynağı bulamıyorum). Neden bu/davamda çalışıyor? – hengxin

+1

Çok iş parçacıklı çalışmak için 'values ​​()' seçeneğine ihtiyacınız var ve Javadoc diyor ki: "Görünümün yineleyicisi, ConcurrentModificationException'u asla atmayacak" yineleyici "bir yineleyici ve yineleyicinin yapısı üzerine var olan öğelerin geçişini garanti eder ve Yapımdan sonra herhangi bir değişikliği yansıtabilir (ancak bunun garantisi yoktur). " – Thilo

0

onun yerine bir HashMap ConcurrentHashMap kullanmak ve farklı iş parçacıkları tarafından eşzamanlı okuma ve modifikasyon korumak. Aşağıdaki uygulamaya bakınız. İplik 1 ve iplik 2'nin aynı anda okuması ve yazması mümkün değildir. İş parçacığı 1, bir diziye Map'den değerler çıkarırken, storeInMap (K, V) 'i çağıran tüm diğer iş parçacıkları, ilk iş parçacığı ile işlenene kadar haritayı askıya alır ve bekler.

Not: Bu bağlamda senkronize yöntem kullanmıyorum; Senkronize yöntemi tamamen devre dışı bırakmıyorum ama dikkatli bir şekilde kullanırdım. Senkronize edilmiş bir yöntem aslında 'bu' üzerinde kilit almak ve yöntemin süresi boyunca tutmak için sözdizimi şekeridir.

private Map<K, V> map = new ConcurrentHashMap<K, V>(); 

// thread 1 
public V[] pickRandom() { 
    int size = map.size(); // size > 0 
    synchronized(map) { 
     V[] value_array = map.values().toArray(new V[size]); 
    } 
    Random rand = new Random(); 
    int start = rand.nextInt(size); 
    int end = rand.nextInt(size); 
    return value_array[start .. end - 1] 
} 

// thread 2 
public void storeInMap(K, V) { 
    synchronized(map) { 
     map.put(K,V); 
    } 
} 
+1

Zaten 'ConcurrentHashMap' kullanıyorsanız' syncronized' engelleniyor? –

İlgili konular