关于Servlet的线程不安全的原因及防范方法
2008-06-02 14:45
337 查看
写了一个Servlet,用到了一个实例变量的HashMap对象,才发现,当第一次访问了该Servlet后,再次访问的时候,HashMap里是有值的,出于好奇,做了个测试,发现当第一次访问Servlet的时候,执行顺序是:调用默认构造方法-->Init()由容器实例化Servlet-->doGet()/doPost()响应请求, 当再次访问这个Servlet的时候,就只会执行doGet()/doPost()了,还是用的原来的那个实例,所以实例变量里有值是正常的, 只有当WebService(例如Tomcat)关闭的时候或Reloading的时候(即覆盖CLASS文件),Servlet才会去调用destroy()方法去销毁实例,此时再去访问则需要重新init().
查了点资料发现:Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行,如图1所示。
图1 Servlet线程池
这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。所以在用Servlet构建的Web应用时如果不注意线程安全的问题,会使所写的Servlet程序有难以发现的错误。
图3 Servlet实例的JMM模型
下面根据图3所示的内存模型,来分析当用户a和b的线程(简称为a线程、b线程)并发执行时,Servlet实例中所涉及变量的变化情况及线程的执行情况,如图4所示。
图4 Servlet实例的线程调度情况
我自己试了下,发现真的会发生这种情况,即a浏览器输入Username=a,最后却输出了b,由此可见Servlet的线程是不安全的,为了避免这种不安全的事发生,所以在Servlet里不要使用实例变量或使用synchronized关键字(需牺牲性能,不推荐),除非是实例变量不参加计算或显示(例如 数据库连接或日志对象).
参考文章:http://www.99inf.net/softwaredev/java/52247.htm
http://www.99inf.net/softwaredev/java/52247.htm
查了点资料发现:Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行,如图1所示。
图1 Servlet线程池
这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。所以在用Servlet构建的Web应用时如果不注意线程安全的问题,会使所写的Servlet程序有难以发现的错误。
String instanceVariable; public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { instanceVariable = request.getParameter("Username"); try{ Thread.sleep (5000); } catch(Exception e){} response.getWriter().write(instanceVariable); response.getWriter().close(); }
图3 Servlet实例的JMM模型
下面根据图3所示的内存模型,来分析当用户a和b的线程(简称为a线程、b线程)并发执行时,Servlet实例中所涉及变量的变化情况及线程的执行情况,如图4所示。
调度时刻 | a线程 | b线程 |
T1 | 访问Servlet页面 | |
T2 | 访问Servlet页面 | |
T3 | output=a的输出username=a休眠5000毫秒,让出CPU | |
T4 | output=b的输出(写回主存)username=b休眠5000毫秒,让出CPU | |
T5 | 在用户b的浏览器上输出a线程的username的值,a线程终止。 | |
T6 | 在用户b的浏览器上输出b线程的username的值,b线程终止。 |
我自己试了下,发现真的会发生这种情况,即a浏览器输入Username=a,最后却输出了b,由此可见Servlet的线程是不安全的,为了避免这种不安全的事发生,所以在Servlet里不要使用实例变量或使用synchronized关键字(需牺牲性能,不推荐),除非是实例变量不参加计算或显示(例如 数据库连接或日志对象).
参考文章:http://www.99inf.net/softwaredev/java/52247.htm
http://www.99inf.net/softwaredev/java/52247.htm
相关文章推荐
- 安全防范基础:关于电脑木马程序隐藏一个的新方法
- 线程安全问题出现的原因和解决方法
- servlet线程不安全的原因
- 关于servlet的线程安全问题
- 关于单实例多线程的servlet的线程安全问题的探讨
- 关于java Servlet,Struts,springMVC 的线程安全问题
- 关于servlet的线程安全问题
- 第10天(就业班) servlet的映射路径、生命周期、自动加载+init方法+线程并发安全、servletconfig对象+servletcontext对象入门、servletcontext对象的使
- Servlet线程不安全及解决方法【转】
- 关于hibernate的session的线程不安全的解决方法
- Servlet为什么是线程不安全的?
- Servlet的线程安全问题
- android 关于利用签名的SHA1进行安全校验的方法之一(推荐)
- 关于解决关闭eclipse之后tomcat无法启动以及找不到javax.servlet的方法
- WCF学习:关于数据类(DataContract)中逻辑方法或复写方法未被初始化的原因及解决方案
- 关于Android Force Close 出现的原因 以及解决方法
- 刨根问底Java多线程系列:线程不安全的最根本的原因是什么
- servlet 与线程安全问题 探讨
- Windows2008 IIS7.5的虚拟空间,Application_Start中方法和后台线程写入文件被拒绝,什么原因?
- Servlet线程安全问题学习总结