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();/* */ }/* */ } 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); } } }/* */ protected final ConcurrentMap sessionMap;/* */ protected final ConcurrentMap> pageSessionMap;/* 527 */ this.sessionMap = new ConcurrentHashMap();/* */ /* 535 */ this.pageSessionMap = new ConcurrentHashMap();
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.