ConcurrentModificationException异常解决办法

今天在写一个带缓存功能的访问代理程序时出现了java.util.ConcurrentModificationException异常, 因为该异常是非捕获型异常而且很少见,所以费了些时间才找到问题所在,原来在通过Iterator进行遍历的时候,如果直接对HashMap进行操作后,再继续用之前的Iterator进行遍历就会出现这个异常,表示其HashMap已经被修改。

源程序代码片段如下:caches为一个HashMap对象

1  String sameKeyPart = accesserClassName + "@" + methodName + "@" + parameterKeyString + "@";
2       Iterator keys = caches.keySet().iterator();
3       String key = null;
4       while (keys.hasNext()) ...{
5         key = (String) keys.next();
6         if (key.startsWith(sameKeyPart)) ...{
7           caches.remove(key);
8         }
9       }

解决办法为通过其相应的Iterator进行删除就可以了,修改后代码片段如下:

1 String sameKeyPart = accesserClassName + "@" + methodName + "@" + parameterKeyString + "@";
2       Iterator keys = caches.keySet().iterator();
3       String key = null;
4       while (keys.hasNext()) ...{
5         key = (String) keys.next();
6         if (key.startsWith(sameKeyPart)) ...{
7           keys.remove();
8         }
9       }

2、

撰写多线程代码时,你遇到过多少次下面的提示:
Exception in thread "main" java.util.ConcurrentModificationException

这个异常产生的原因有几个。一是直接对集合调用删除操作而不是在枚举器上。二是不同的线程试图对集合进行增删操作的时候。

这个解决办法的第一步就是同步代码,使得你在枚举的时候其它的线程不能增删记录。但是如果每个枚举过程要进行复杂的计算或者是数据库访问的一部分的话,这个同步就会导致可怕的后果。为了减少负面影响,可以拷贝一个只读的枚举器,去掉同步,然后采用下列代码所示的方法:

 1 private List list;
 2   public void add(Object obj) {
 3   synchronized(list) {
 4   list.add(obj);
 5   }
 6   }
 7   public void perform( ) {
 8   Iterator iterator = null;
 9   synchronized(list) {
10   iterator = new CopiedIterator(list.iterator( ));
11   }
12   while(iterator.hasNext( )) {
13   // perform resource or cpu hungry work
14   }
15   }

重要的是记住,CopiedIterator不是一个克隆,只是一个只读的拷贝,所以它并没有保持原有的全部功能。最重要的是,不能再调用CopiedIterator.remove方法了。CopiedIterator.remove的实现如下:

 1 public class CopiedIterator implements Iterator {
 2   private Iterator iterator = null;
 3   public CopiedIterator(Iterator itr) {
 4   LinkedList list = new LinkedList( );
 5   while(itr.hasNext( )) {
 6   list.add(itr.next( ));
 7   }
 8   this.iterator = list.iterator( );
 9   }
10   public boolean hasNext( ) {
11   return this.iterator.hasNext( );
12   }
13   public void remove( ) {
14   throw new UnsupportedOperationException("This is a read-only iterator.
15   ");
16   }
17   public Object next( ) {
18   return this.iterator.next( );
19   }
20   }

枚举器的只读拷贝将用在同步状态上的时间减少到最小,因此可以增强全局的效率。

3、

当使用 fail-fast iterator 对 Collection 或 Map 进行迭代操作过程中尝试直接修改 Collection / Map 的内容时,即使是在单线程下运行,  java.util.ConcurrentModificationException 异常也将被抛出。
  Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。

  所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

  有意思的是如果你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.

1  import java.util.*;   
2    
3  public final class MyTest   
4  {   
5      private static HashMap p_mapList = new HashMap(2);   
6      private MyTest(){}   
7      public static void init(){   
8          // If only there are more than one element in Map,    
9          // the ConcurrentModificationException will not be   
10          // thrown.   
11          p_mapList.put(new String("hello"),new String("world"));   
12          p_mapList.put(new String("goto"),new String("hell"));   
13      }   
14      public static void clear() throws Exception{   
15          Iterator pTmpKeys = null;   
16          Long pTmpKeyLong;   
17          pTmpKeys = p_mapList.keySet().iterator();   
18          String pCurKey = null;   
19          String pCurObj = null;   
20          while(pTmpKeys.hasNext()){   
21              pCurKey = (String) pTmpKeys.next();   
22              pCurObj = (String) p_mapList.get(pCurKey);   
23    
24              p_mapList.put(pCurKey,null);   
25              // You can not remove element in Map object directly.   
26              //p_mapList.remove(pCurKey);   
27              // But you can remove current element by iterator itself.   
28              pTmpKeys.remove();   
29    
30              System.out.println(pCurKey + " removed.");   
31          }   
32          System.out.println(p_mapList.size() +    
33                              " entries left after iterator.");   
34          pTmpKeys = null;   
35      }   
36      public static void main(String[] args)   
37                                  throws Exception{   
38          MyTest.init();   
39          MyTest.clear();   
40      }   
41  }

时间: 2024-10-24 18:02:55

ConcurrentModificationException异常解决办法的相关文章

java.util.ConcurrentModificationException 异常解决办法及原理

最近在修程序的bug,发现后台抛出以下异常: Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793) at java.util.HashMap$KeyIterator.next(HashMap.java:828) at com.keyman.demo.test.ClearResult

java.util.ConcurrentModificationException的解决办法

今天在使用iterator.hasNext()操作迭代器的时候,当迭代的对象发生改变,比如插入了新数据,或者有数据被删除. 编译器报出了以下异常: Exception in thread "main" java.util.ConcurrentModificationException at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719) at java.util.LinkedHash

org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session异常解决办法

org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session异常解决办法 为什么还会说已经存在相同的session了呢.然后每次将项目重启后第一次编辑的时候问题不会触发,只有当第二次操作的时候才会出现这个问题. 解决办法:关闭session.好好检查操作完成后有没有关闭会话. org.hibernat

Android主线程不能访问网络异常解决办法

从两个方面说下这个问题: 1. 不让访问网络的原因 2. 解决该问题的办法 不让访问网络的原因: 由于对于网络状况的不可预见性,很有可能在网络访问的时候造成阻塞,那么这样一来我们的主线程UI线程 就会出现假死的现象,产生很不好的用户体验.所以,默认的情况下如果直接在主线程中访问就报出了这个异常,名字是NetworkOnMainThreadException 解决该问题的办法 1. 独立线程 2. 异步线程AsyncTask 3. StrictMode修改默认的策略 1) 独立线程的办法 启动一个

Spring 配置异常解决办法之Referenced file contains errors

Spring 配置异常解决办法之Referenced file contains errors (http://www.sp Referenced file contains errors (http://www.springframework.org/schema/beans/spring-beans-3.0.xsd). For more information, right click on the message in the Problems View and select "Show

Java AOP nested exception is java.lang.NoClassDefFoundError: org/aopalliance/aop/Advice || Error creating bean with name 'org.springframework.aop.aspectj.AspectJPointcutAdvisor#0' 两个异常解决办法

贴出applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://ww

cosbench read异常解决办法。 Unable to verify integrity of data download. Client calculated content hash didn&#39;t match hash calculated by Amazon S3. The data may be corrupt.

问题:cosbench read测试failed 报错如下 Cosbench v0.4.2.c4 against Ceph (Hammer) / radosgw / HAproxy's HTTP end point. All seems fine except that at the end of the read test, it failed the task and gave following errors com.amazonaws.AmazonClientException: Una

关于 java 上传,下载和导入报java.lang.IllegalStateException异常解决办法

java.lang.IllegalStateException异常解决办法 最近在使用response.sendRedirect()时出现如下错误:java.lang.IllegalStateException            org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:423) 经过分析.查看jdk文档终于找到解决的办法,在response.sendRedirect()方法后加

The APR based Apache Tomcat Native library 异常解决办法

tomat在linux服务器上启动报The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/local/jdk1.6.0_26/jre/lib/i386/server:/usr/local/jdk1.6.0_26/jre/lib/i386:/usr/local