这是我前些日子坐开发的时候碰到的一个异常:
java.util.ConcurrentModificationException
java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
java.util.AbstractList$Itr.next(AbstractList.java:343)
com.ntcsoft.article.action.ArticleIndexAction.seaByArtType(ArticleIndexAction.java:47)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
解决方法:
引用网上的解决方案:
http://jackhlp.blog.163.com/blog/static/11796218200710221172542/今天在项目的中有一个需求,需要在一个Set类型的集合中删除满足条件的对象,这时想当然地想到直接调用Set的remove(Object o)方法将指定的对象删除即可,测试代码:
public class Test {
public static void main(String[] args) {
User user1 = new User();
user1.setId(1);
user1.setName("zhangsan");
User user2 = new User();
user2.setId(2);
user2.setName("lisi");
Set userSet = new HashSet();
userSet.add(user1);
userSet.add(user2);
for (Iterator it = userSet.iterator(); it.hasNext();) {
User user = (User) it.next();
if (user.getId() == 1) {
userSet.remove(user);
}
if (user.getId() == 2) {
user.setName("zhangsan");
}
}
for(Iterator it = userSet.iterator(); it.hasNext(); ) {
User user = (User) it.next();
System.out.println(user.getId() + "=>" + user.getName());
}
}
}
但运行程序的时候,却发现出错了:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$KeyIterator.next(Unknown Source)
at test.Test.main(Test.java:23)
从API中可以看到List等Collection的实现并没有同步化,如果在多线程应用程序中出现同时访问,而且出现修改操作的时候都要求外部操作同步化;调用Iterator操作获得的Iterator对象在多线程修改Set的时候也自动失效,并抛出java.util.ConcurrentModificationException。这种实现机制是fail-fast,对外部的修改并不能提供任何保证。
网上查找的关于Iterator的工作机制。Iterator是工作在一个独立的线程中,并且拥有一个 mutex锁,就是说Iterator在工作的时候,是不允许被迭代的对象被改变的。Iterator被创建的时候,建立了一个内存索引表(单链表),这个索引表指向原来的对象,当原来的对象数量改变的时候,这个索引表的内容没有同步改变,所以当索引指针往下移动的时候,便找不到要迭代的对象,于是产生错误。List、Set等是动态的,可变对象数量的数据结构,但是Iterator则是单向不可变,只能顺序读取,不能逆序操作的数据结构,当 Iterator指向的原始数据发生变化时,Iterator自己就迷失了方向。如何才能满足需求呢,需要再定义一个List,用来保存需要删除的对象:
List delList = new ArrayList();
最后只需要调用集合的removeAll(Collection con)方法就可以了。
public class Test {
public static void main(String[] args) {
boolean flag = false;
User user1 = new User();
user1.setId(1);
user1.setName("shangsan");
User user2 = new User();
user2.setId(2);
user2.setName("lisi");
Set userSet = new HashSet();
userSet.add(user1);
userSet.add(user2);
List delList = new ArrayList();
for (Iterator it = userSet.iterator(); it.hasNext();) {
User user = (User) it.next();
if (user.getId() == 1) {
delList.add(user);
}
if (user.getId() == 2) {
user.setName("zhangsan");
}
}
userSet.removeAll(delList);
for(Iterator it = userSet.iterator(); it.hasNext(); ) {
User user = (User) it.next();
System.out.println(user.getId() + "=>" + user.getName());
}
}
}
网上的解决方法2:
http://blog.csdn.net/myyate/archive/2007/12/17/1943040.aspx
今天组长提了一个问题注意点,说删除Set元素的时候,如果按照一下代码则会抛出异常: Set<Object> bb = new HashSet<Object>();
bb.add("23242");
bb.add(Integer.valueOf(45));
Iterator it = bb.iterator();
while(it.hasNext()) {
Object ele = it.next();
bb.remove(ele); //wrong
}
异常如下:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$KeyIterator.next(HashMap.java:827)
at com.learn.test.Test.testIteratorRemove(Test.java:118)
at com.learn.test.Test.main(Test.java:102)
从上可以看出异常是从HashMap的内部类抛出的。
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry<K,V> current; // current entry
.....
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException(); //抛出异常
Entry<K,V> e = current = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
return e;
}
..........
}
我们都知道HashSet其实只基于HashMap来实现的。看HashSet的remove实现方法:
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
再来看HashMap的remove方法:
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
//先取得key的hashcode,根据hashcode在哈希表中找到对应的index,然后在对应的index再遍历table[i]指向的链表,这个链表一般只有一个元素
final Entry<K,V> removeEntryForKey(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
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++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
其实注意到其中间有这么一段代码:modCount++; modCount是这个HashMap结构修改的次数,比如增加,删除等等,再来看上面HashIterator中的expectedModCount,这个是Iterator中期望HashMap元素修改次数,初始值即modCount,HashMap的remove方法执行时,并没有同步当然也无法同步这两个变量值一致,才导致了上面的异常。找到了抛出异常的原因,很明显不能通过这个方法来删除Set中的元素,我们注意到HashIterator也有remove方法,代码如下:
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount; //删除完同步两个变量的值
}
所以尝试一下代码:
Set<Object> bb = new HashSet<Object>();
bb.add("23242");
bb.add(Integer.valueOf(45));
Iterator it = bb.iterator();
while(it.hasNext()) {
it.next();
it.remove();
}
执行成功。所以,执行HashSet的remove方法时,是不可以用Iterator方法遍历一个一个删除的。但是HashSet的remove方法也可以执行成功,就是Set中只有一个元素的时候,因为初始的时候expectedModCount和modCount是相等的。
相关推荐
虽然提供了基本的线程安全性,但它们不是高度优化的并发解决方案,因为所有操作都需要全局锁定,可能导致性能瓶颈。 2. 并发集合(Concurrent Collections): Java的`java.util.concurrent`包提供了更为高效且...
在Java编程中,只读集合(Read-Only Collection)是一种不允许进行修改的集合类型,它提供了安全的数据访问...详细指导将涵盖各种使用场景、最佳实践以及可能遇到的问题和解决方案,帮助你成为一名更优秀的Java开发者。
- **并发容器**:如`ConcurrentHashMap`、`CopyOnWriteArrayList`、`ConcurrentLinkedQueue`等,为并发访问提供了线程安全的解决方案。 - **阻塞队列**:`BlockingQueue`接口,实现生产者消费者模型,如`...
### Java常见问题及解决方案 #### 一、编译错误 **1.1 问题:** "cannot find symbol" **解决:** 这个错误提示通常意味着Java编译器未能找到指定的类、方法或变量。要解决这个问题,需要进行以下几个步骤的检查: ...
如果能查看博客的具体内容,将有助于提供更具体的解决方案。此外,`CoffeeSystem`可能是一个相关的系统或者类库,但没有足够的信息来进一步分析其与`iterator()`问题的联系。 为了更好地调试和解决问题,你可以按照...
本篇将深入探讨HashMap的线程安全问题,并提供相关的解决方案。 首先,我们需要了解HashMap在多线程环境下可能出现的问题: 1. **并发修改异常(ConcurrentModificationException)**:当多个线程同时修改HashMap...
`ConcurrentHashMap`是Java集合框架中的一个关键成员,它在`java.util.concurrent`包下,为并发操作提供了一种高效且线程安全的解决方案。与传统的`synchronized` `HashMap`相比,`ConcurrentHashMap`采用了分段锁的...
这个类位于Java的集合框架中,为程序员提供了灵活且性能优异的键值对存储解决方案。 **哈希表的工作原理** 哈希表基于数组和链表的组合实现。当插入一个新的键值对时,哈希函数会计算键的哈希码,然后根据这个哈希...
当多个线程同时调用`add`方法时,可能会抛出`java.util.ConcurrentModificationException`异常。 **4.2 解决方案** - 使用`Vector`类代替`ArrayList`,因为`Vector`的所有公共方法都进行了同步处理,但这种方法降低...
### 解决方案 1. **Collections.synchronizedMap()**:可以使用`Collections.synchronizedMap()`静态方法将`HashMap`包装成线程安全的`SynchronizedMap`。但请注意,尽管这个方法可以确保并发修改的安全,但仍然...
多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 5、String是最基本的数据类型吗? 基本数据类型包括byte、int、char、long、float、double、boolean和short。 java....
在Java编程语言中,链表主要体现在`LinkedList`类中,它是Java集合框架的一部分,位于`java.util`包下。 链表的基本组成部分是节点(Node),每个节点包含数据元素和指向下一个节点的引用。在Java的`LinkedList`中...