DWR并发异常 java.util.ConcurrentModificationException checkTimeouts

52818 java.util.ConcurrentModificationException
52819 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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值