当两个线程试图修改/访问HashMap中的相同键时会发生什么?

时间:2017-10-24 04:40:14

标签: java multithreading

在并发环境中应该使用ConcurrentHashMap。但是java为普通的HashMap做了什么?

Map map = new HashMap();

// thread 1
map.put("a", 1)

// thread 2
map.put("a", 2)

当试图通过map.get("a")获取价值时,我是否会承诺地图不会被破坏,返回值必须在12

3 个答案:

答案 0 :(得分:4)

如果我理解,问题是关于并发场景中发生了什么,而不是HashMap是否合适(众所周知的不合适)。

在一个非常不受欢迎的场景中发现可以找到哪类问题的最佳方法是分析the source code。例如,在openjdk 1.6实现中:

public V More ...put(K key, V value) {
     if (key == null)
         return putForNullKey(value);
     int hash = hash(key.hashCode());
     int i = indexFor(hash, table.length);
     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
         Object k;
         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
             V oldValue = e.value;
             e.value = value;
             e.recordAccess(this);
             return oldValue;
         }
    }

     modCount++;
     addEntry(hash, key, value, i);
     return null;
 }

如果此方法和属性中缺少同步机制,则可能出现以下一个或多个问题:

1)非确定性回报

该行:

return oldValue;
线程1的

结果可以是旧值(在线程1和2之前调用),可以是2或null。如果线程2 addEntry(散列,键,值,i)完全发生或不发生,则它依赖于它。当然,线程2也存在同样的问题。

2)未确定的索引和重复的密钥

int i = indexFor(hash, table.length);

取决于table.length。因此,根据是否存在此哈希的先前值,i索引可以分散两个调用(取决于另一个线程中addEntry(hash,key,value,i)的执行。

3)破碎的尺寸和一致性

预计连续调用put(“a”,...)不会改变映射的大小(至少在第一次调用之后没有键“a”的条目)。但是,根据两个线程的race conditions,地图大小一致性可能会被破坏,即keyset的大小与地图大小的大小不同。用于散列键的其他变量,因为modCount可能会变得不一致,以及将来调用put和get。

那么,在评论问题中如何正确地说,由于意外行为和对内部结构的破坏,在并发场景中使用Map是完全消失的。

答案 1 :(得分:3)

Hashmaps不是线程安全的,不能保证会发生什么。 这取决于线程的执行顺序。

答案 2 :(得分:3)

不在读取模式下对HashMap的多线程访问可能导致未定义的行为。

使用同步构造Collections.synchronizedMap(new HashMap())