本科的毕业设计做的是一个Android小程序--郑大小秘书,做完这个,感觉是对自己整体能力的提升。期末结束了,大家对考试成绩都特别的关注,时不时的查看一下自己的成绩。由于查分人数的增加,昨天发现服务器已经宕掉。看了一下对应的tomcat日志,看到一下错误:
Jan 16, 2014 8:59:49 AM org.apache.catalina.core.StandardWrapperValve invoke SEVERE: Servlet.service() for servlet [MyClass] in context with path [/ZzuServer] threw exception java.util.ConcurrentModificationException at java.util.ArrayList.writeObject(ArrayList.java:713) at sun.reflect.GeneratedMethodAccessor32.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1429) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1175) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347) at cn.edu.zzu.servlet.MyClass.doPost(MyClass.java:47) at javax.servlet.http.HttpServlet.service(HttpServlet.java:647) at javax.servlet.http.HttpServlet.service(HttpServlet.java:728) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:724)
根据日志以及 java.util.ConcurrentModificationException快速的定位到相应类的相应行,查看了一下代码。因为以前自己也遇到这种问题,所以也很快的发现是自己定义的一个静态集合变量,在多个线程中并发访问,违背了java中的fail-fast原则,虽然自己在代码中使用synchronized来使线程同步,但是静态变量的操作却不仅仅是在synchronized方法中,在synchronized方法外,有对集合变量的传递,这就造成了传递前可能变量内容已经更改。
小秘书的目前用户有尽4000人,每天活跃人数900多人,启动5000多次,放假之前的访问量大概是当前的一半,所以查成绩时一人查询成绩时服务器完成上一次成绩查询操作并返回用户结果,那么这个错误就将被隐藏起来,现在访问量大了之后,问题就暴露出来了。去掉集合变量前的static后,每次调用新建一个变量,问题解决,也不在出现这样的错误。
通过这一次的小事故,使我进一步对java中变量的修饰符有了进一步的认识,修饰变量前,一定要考虑变量的是否公开(public,private等),是否需要支持并发访问(synchronized,static等),是否只能更改一次(final)等。每个修饰符都有自己独有的功能和意义,使用时一定要分清楚。