现在的位置: 首页 > 综合 > 正文

java同步集合类以及java.util.ConcurrentModificationException的原因

2018年06月05日 ⁄ 综合 ⁄ 共 2631字 ⁄ 字号 评论关闭

首先通过一个案例来引出要讨论的话题:

public class User {

	private Integer uid;
	private String uname;
	
	public User() {
	}
	public User(Integer uid, String uname) {
		this.uid = uid;
		this.uname = uname;
	}
	public Integer getUid() {
		return uid;
	}
	public void setUid(Integer uid) {
		this.uid = uid;
	}
	public String getUname() {
		return uname;
	}
	public void setUname(String uname) {
		this.uname = uname;
	}
	
	
}

public class CollectionModifyExceptionTest {
	public static void main(String[] args) {

		/*创建一个集合*/
		Collection<User> users = new ArrayList<User>();

		/*往集合中添加数据*/
		users.add(new User(1, "习近平"));
		users.add(new User(1, "李克强"));
		users.add(new User(1, "王岐山"));
		
		/*获取迭代器*/
		Iterator<User> itrUsers = users.iterator();
		
		/*遍历*/
		while (itrUsers.hasNext()) {
			
			User user = (User) itrUsers.next();
			
			if ("王岐山".equals(user.getUname())) {
				users.remove(user);
			} else {
				System.out.println(user);
			}
		}
	}
}

程序运行的结果:

为什么会报错呢?

原因:不可以对iterator相关的地方做添加或删除操作。

我们来看看iterator()方法:

public Iterator<E> iterator() {  
   return new Itr();  
}  

在new Itr()时有一个关键操作:

/** 
  * The modCount value that the iterator believes that the backing 
  * List should have.  If this expectation is violated, the iterator 
  * has detected concurrent modification. 
  */  
 int expectedModCount = modCount; 

在来看看List集合中的remove()方法:

public boolean remove(Object o) {  
 if (o == null) {  
            for (int index = 0; index < size; index++)  
  if (elementData[index] == null) {  
      fastRemove(index);  
      return true;  
  }  
 } else {  
     for (int index = 0; index < size; index++)  
  if (o.equals(elementData[index])) {  
      fastRemove(index);  
      return true;  
  }  
        }  
 return false;  
    }  
  
   
  
private void fastRemove(int index) {  
        modCount++;  
        int numMoved = size - index - 1;  
        if (numMoved > 0)  
            System.arraycopy(elementData, index+1, elementData, index,  
                             numMoved);  
        elementData[--size] = null; // Let gc do its work  
    }  

在看一下iterator.next()操作的源码:

public E next() {  
            checkForComodification();  
     try {  
  E next = get(cursor);  
  lastRet = cursor++;  
  return next;  
     } catch (IndexOutOfBoundsException e) {  
  checkForComodification();  
  throw new NoSuchElementException();  
     }  
 }  
  
   
  
final void checkForComodification() {  
     if (modCount != expectedModCount)  
  throw new ConcurrentModificationException();  
 }  
 

从上述源码中可以看出:iterator 时 将expectedModCount = modCount 在remove()时 modCount++ 导致next()时抛出异常:

if (modCount != expectedModCount)  
  throw new ConcurrentModificationException(); 

一旦添加或删除元素后,modCount,expectedModCount这两个值就会不一样,当next时就会报错。

解决方案一:

再定义一个集合,在遍历过程中,把要删除的元素,添加到该集合中,迭代完成之后,直接删除这个集合中的内容即可。

解决方法二:

使用java5中的可同步集合类:CopyOnWriteArrayList<T>

修改代码如下:

public class CollectionModifyExceptionTest {
	public static void main(String[] args) {

		/*创建一个集合*/
		//Collection<User> users = new ArrayList<User>();
		Collection<User> users = new CopyOnWriteArrayList<User>();

		/*往集合中添加数据*/
		users.add(new User(1, "习近平"));
		users.add(new User(1, "李克强"));
		users.add(new User(1, "王岐山"));
		
		/*获取迭代器*/
		Iterator<User> itrUsers = users.iterator();
		
		/*遍历*/
		while (itrUsers.hasNext()) {
			
			User user = (User) itrUsers.next();
			
			if ("王岐山".equals(user.getUname())) {
				users.remove(user);
			} else {
				System.out.println(user);
			}
		}
	}
}

抱歉!评论已关闭.