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
- HashMap常见并发问题
- HashMap的并发问题
- 解决HashMap并发问题
- HashMap多线程并发问题分析
- HashMap多线程并发问题分析
- HashMap多线程并发问题分析
- HashMap多线程并发问题分析
- HashMap多线程并发问题分析
- HashMap多线程并发问题分析
- HashMap多线程并发问题分析
- HashMap并发出现的问题
- HashMap常见面试问题总结
- HashMap常见面试问题总结
- 图说HashMap多线程并发问题分析
- JAVA HASHMAP并发访问出现的问题
- 常见事务并发问题以及处理方法
- 常见的并发问题处理方式
- 并发场景下HashMap死循环导致CPU100%的问题
- linux AWK命令(2)
- 超级简单的纯js 象棋,看一遍你也会写
- Android中Context详解 ---- 你所不知道的Context
- leetcode Compare Version Numbers
- 观察者模式
- HashMap常见并发问题
- 性能监控Ganglia搭建
- Service 官方详解(从官方文档翻译而来)
- ORA-28000: the account is locked的问题
- ios8中用swift实现文本输入框适应键盘高度变化
- app-framework学习--公用加载、刷新
- 俩种将ppt转换成pdf的方法
- enum枚举与int string类型之间的相互转换
- 怎样扩展摩托罗拉的手机内存