首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

DWR并发错误 java.util.ConcurrentModificationException checkTimeouts

2012-10-12 
DWR并发异常 java.util.ConcurrentModificationException checkTimeouts52818 java.util.ConcurrentModifi

DWR并发异常 java.util.ConcurrentModificationException checkTimeouts

52818 java.util.ConcurrentModificationException52819     at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)52820     at java.util.HashMap$ValueIterator.next(HashMap.java:822)52821     at org.directwebremoting.impl.DefaultScriptSessionManager.checkTimeouts(DefaultScriptSessionManager.java:179)52822     at org.directwebremoting.impl.DefaultScriptSessionManager.maybeCheckTimeouts(DefaultScriptSessionManager.java:163)52823     at org.directwebremoting.impl.DefaultScriptSessionManager.getScriptSession(DefaultScriptSessionManager.java:50)52824     at org.directwebremoting.impl.DefaultWebContext.getScriptSession(DefaultWebContext.java:83)52825     at org.directwebremoting.dwrp.BaseCallMarshaller.marshallOutbound(BaseCallMarshaller.java:305)52826     at org.directwebremoting.servlet.PlainCallHandler.handle(PlainCallHandler.java:53)52827     at org.directwebremoting.servlet.UrlProcessor.handle(UrlProcessor.java:101)52828     at org.directwebremoting.servlet.DwrServlet.doPost(DwrServlet.java:146)52829     at com.netease.photo.webapp.web.servlets.PhotoDwrServlet.doPost(PhotoDwrServlet.java:51)52830     at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)52831     at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)52832     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)52833     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)52834     at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:265)52835     at com.netease.photo.security.filter.NEAccessInfoFilter.doFilter(NEAccessInfoFilter.java:55)52836     at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275)52837     at com.netease.photo.security.filter.NEFilterSecurityInterceptor.doFilter(NEFilterSecurityInterceptor.java:49)52838     at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275)52839     at com.netease.photo.security.filter.NEAnonymousProcessingFilter.doFilter(NEAnonymousProcessingFilter.java:234)52840     at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275)52841     at com.netease.photo.security.filter.NEAuthenticationProcessingFilter.doFilter(NEAuthenticationProcessingFilter.java:300)52842     at org.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:275)52843     at com.netease.photo.security.filter.NEExceptionTranslationFilter.doFilter(NEExceptionTranslationFilter.java:62)



查看源代码可以发现,异常代码块
/*     */   protected void checkTimeouts()/*     */   {/* 174 */     long now = System.currentTimeMillis();/* 175 */     List timeouts = new ArrayList();/*     */ /* 177 */     for (Iterator it = this.sessionMap.values().iterator(); it.hasNext(); )/*     */     {/* 179 */       DefaultScriptSession session = (DefaultScriptSession)it.next();/*     */ /* 181 */       if (session.isInvalidated())/*     */       {/*     */         continue;/*     */       }/*     */ /* 186 */       long age = now - session.getLastAccessedTime();/* 187 */       if (age > this.scriptSessionTimeout)/*     */       {/* 189 */         timeouts.add(session);/*     */       }/*     */     }/*     */ /* 193 */     for (Iterator it = timeouts.iterator(); it.hasNext(); )/*     */     {/* 195 */       DefaultScriptSession session = (DefaultScriptSession)it.next();/* 196 */       session.invalidate();/*     */     }/*     */   }


由于iterator报这个错是因为再iterator的过程中有添加删除操作
,查看添加删除sessionMap数据的代码
  public RealScriptSession getScriptSession(String id)    {        maybeCheckTimeouts();        synchronized (sessionLock)        {            DefaultScriptSession scriptSession = (DefaultScriptSession) sessionMap.get(id);            if (scriptSession == null)            {                scriptSession = new DefaultScriptSession(id, this);              *  sessionMap.put(id, scriptSession);*            }            else            {                scriptSession.updateLastAccessedTime();            }            return scriptSession;        }    }    protected void invalidate(RealScriptSession scriptSession)    {        // Can we think of a reason why we need to sync both together?        // It feels like a deadlock risk to do so        synchronized (sessionLock)        {            *RealScriptSession removed = (RealScriptSession) sessionMap.remove(scriptSession.getId());*            if (!scriptSession.equals(removed))            {                log.debug("ScriptSession already removed from manager. scriptSession=" + scriptSession + " removed=" + removed);            }            int removeCount = 0;            for (Iterator it = pageSessionMap.values().iterator(); it.hasNext();)            {                Set pageSessions = (Set) it.next();                boolean isRemoved = pageSessions.remove(scriptSession);                if (isRemoved)                {                    removeCount++;                }            }            if (removeCount != 1)            {                log.debug("DefaultScriptSessionManager.invalidate(): removeCount=" + removeCount + " when invalidating: " + scriptSession);            }        }    }


发现已经做了sessionlock,并且在其他外部类中没有调用。
http://directwebremoting.org/jira/browse/DWR-536
这个是我们遇到的问题.
The problem is when using an iterator of a synchonized map; when using an iterator, the map (even when it is synchronized) should be synchronized while iterating...
即使对hashmap外部做了synchronized,在iterator的时候hashmap还是不安全的。(这里不安全的原因还在调查,估计跟jvm有关)
有两种解决方案
1.对hashmap在iterator之前做一次拷贝
2.对hashmap做同步,可以使用concurrenthashmap

在dwr后面的几次版本更新中已经有了修改
/*     */   protected final ConcurrentMap sessionMap;/*     */   protected final ConcurrentMap> pageSessionMap;/* 527 */     this.sessionMap = new ConcurrentHashMap();/*     */ /* 535 */     this.pageSessionMap = new ConcurrentHashMap();



为什么使用conCurrentMap能够保证安全?

首先,从问题代码讲起
for (Iterator it = sessionMap.values().iterator(); it.hasNext();)
出问题的原因是iterator在做浅拷贝的时候,sessionMap有数据变化(添加、删除)
导致modCount和expectedModCount不相等,判断了checkForComodification()后,抛出异常
在使用ConcurrentMap的时候,
ConcurrentHashMap使用了不同于传统集合的快速失败迭代器(见之前的文章《JAVA API备忘---集合》)的另一种迭代方式,我们称为弱一致迭代器。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变,更重要的,这保证了多个线程并发执行的连续性和扩展性,是性能提升的关键。 参考文献地址:[url]http://pengtyao.iteye.com/blog/1074271[/url][url]http://stackoverflow.com/questions/3768554/is-iterating-concurrenthashmap-values-thread-safe[/url]It is guaranteed that things will not break if you do this (that's part of what the "concurrent" in ConcurrentHashMap means). However, there is no guarantee that one thread will see the changes to the map that the other thread performs (without obtaining a new iterator from the map). The iterator is guaranteed to reflect the state of the map at the time of it's creation. Futher changes may be reflected in the iterator, but they do not have to be.


热点排行