HashMap常见并发问题

来源:互联网 发布:导弹牵引车知乎 编辑:程序博客网 时间:2024/05/20 12:25

HashMap是非线程安全的,在多线程使用HashMap时一定要注意。具体详解可参考http://alex09.iteye.com/blog/539545

一.多线程put()

@Testpublic void test1() throws InterruptedException {    final Map<Integer, String> map = new HashMap<Integer, String>();    for (int i = 0; i < 1000; i++) {        new Thread() {            public void run() {                for (int i = 0; i < 10; i++) {                    map.put(i, i + "");                }            };        }.start();    }    Thread.sleep(10000);// 等线程执行完    List<Integer> keyList = new ArrayList<Integer>(map.keySet());    Collections.sort(keyList);    logger.debug("size=" + map.size() + ",keys=" + keyList);}

执行结果如下:

2015-03-04 10:39:34.921 DEBUG ConcurrentHashMapTest:56 - size=17,keys=[0, 1, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 9]

可以看到数据发生了不一致。

甚至可能会出现死循环,参考《疫苗:Java HashMap的死循环》


二. ConcurrentModificationException异常

如果HashMap在迭代读取的过程中,发生结构性修改操作(增/删,不包括改),则会抛出ConcurrentModificationException异常。这是因为HashMap在进行结构性修改时会用modCount变量来记录操作个数,每次结构性修改时会+1。

    /**     * The number of times this HashMap has been structurally modified     * Structural modifications are those that change the number of mappings in     * the HashMap or otherwise modify its internal structure (e.g.,     * rehash).  This field is used to make iterators on Collection-views of     * the HashMap fail-fast.  (See ConcurrentModificationException).     */    transient int modCount;

例如remove()操作:

final Entry<K,V> removeEntryForKey(Object key) {if (size == 0) {return null;}int hash = (key == null) ? 0 : hash(key);int i = indexFor(hash, table.length);Entry<K,V> prev = table[i];Entry<K,V> e = prev;while (e != null) {Entry<K,V> next = e.next;Object k;if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k)))) {modCount++;//删除时,结构性修改记录数+1size--;if (prev == e)table[i] = next;elseprev.next = next;e.recordRemoval(this);return e;}prev = e;e = next;}return e;}

还有put()操作:

public V put(K key, V value) {if (table == EMPTY_TABLE) {inflateTable(threshold);}if (key == null)return putForNullKey(value);int hash = hash(key);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也不会+1}}modCount++;//新增时,结构性修改操作才会+1addEntry(hash, key, value, i);return null;}



而在迭代开始的时候,会记下当前的modCount值给expectedModCount变量,如果在迭代的过程中HashMap发生了结构性修改操作则会抛出ConcurrentModificationException异常。

final Entry<K,V> nextEntry() {if (modCount != expectedModCount)throw new ConcurrentModificationException();Entry<K,V> e = next;if (e == null)throw new NoSuchElementException();if ((next = e.next) == null) {Entry[] t = table;while (index < t.length && (next = t[index++]) == null);}current = e;return e;}
再次强调一下,结构性修改操作是增和删,不包括修改。

2.1 增加

@Testpublic void add() {Map<Integer, String> map = new HashMap<Integer, String>();for (int i = 0; i < 10; i++) {map.put(i, i + "");}int i = 1;Iterator<Integer> iter = map.keySet().iterator();while (iter.hasNext()) {int key = iter.next();logger.debug("[" + (i++) + "]" + key);map.put(key + 100, key + "");}}


执行上面的代码,会抛出ConcurrentModificationException异常。因为put()操作会增加新的<Key, Value>,导致结构性修改。


2.2 修改

@Testpublic void add() {Map<Integer, String> map = new HashMap<Integer, String>();for (int i = 0; i < 10; i++) {map.put(i, i + "");}int i = 1;Iterator<Integer> iter = map.keySet().iterator();while (iter.hasNext()) {int key = iter.next();logger.debug("[" + (i++) + "]" + key);map.put(key, key + "-hello");}}
执行上面的代码,则可以正常执行。因为之前的key已存在,put()只是修改了value,并不会导致结构性修改。

2.2 删除

@Testpublic void remove() {    Map<Integer, String> map = new HashMap<Integer, String>();    for (int i = 0; i < 10; i++) {        map.put(i, i + "");    }    int i = 1;    Iterator<Integer> iter = map.keySet().iterator();    while (iter.hasNext()) {        int key = iter.next();        logger.debug("[" + (i++) + "]" + key);        map.remove(1);//迭代过程中不允许修改    }}

执行上面的代码,会抛出ConcurrentModificationException异常。因为remove()操作导致结构性修改。




0 0
原创粉丝点击