HashMap常见并发问题

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

一.多线程put()

@Test
public 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++;//删除时,结构性修改记录数+1
			size--;
			if (prev == e)
				table[i] = next;
			else
				prev.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++;//新增时,结构性修改操作才会+1
	addEntry(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 增加

@Test
public 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 修改

@Test
public 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 删除

@Test
public 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()操作导致结构性修改。




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值