Servlet线程安全问题学习总结
来源:互联网 发布:阮铭 两少一宽 知乎 编辑:程序博客网 时间:2024/06/07 11:32
首先说明的是Servlet不是线程安全的。
Servlet容器在启动或第一次请求这个servlet时,Servlet容器会创建一个Servlet实例。请求完成后,servlet实例会被纳入servlet容器的线程池进行管理。所以在默认情况下,当多个请求是共享一个servlet实例的。
因此在多个线程同时访问一个servlet实例时,可能会发生多线程同时访问一个资源的情况,数据可能会变得不一致。也就存在了线程的安全问题。
package com.hg.javafan;
import javax.servlet. *;
import javax.servlet.http. *;
import java.io. *;
public class ServletTest extends HttpServlet
{
private static final long serialVersionUID = 1L;
PrintWriter output;
public void service (HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
{
String username;
response.setContentType ("text/html; charset=GBK");
username = request.getParameter ("username");
output = response.getWriter ();
try
{
Thread.sleep (5000); //在这设置一个延时
}
catch (InterruptedException e)
{
}
output.println("用户名:"+username+"<BR>");
}
}
在web.xml中配置好servlet
同时启动两个IE窗口
a: http://localhost: 8080/servlet/test?username=a
b: http://localhost: 8080/servlet/test?username=b
输出结果:先打开的窗口没任何输出,后开打的窗口输出两行语句。
原因:Java的内存模型设计。在系统中存在一个主内存(Main Memory),java中所有的实例变量都存放在主内存中,对于所有的线程都是共享的。但是每个线程都有自己的工作内存(Working memory),工作内存由两部分组成:缓存和堆栈。 缓存中存放的是主存中变量的拷贝,缓存中的变量拷贝和主存可能存在不同步时候,就是缓存中的修改没有立即更新到主存中; 堆栈中存放的是线程局部变量。堆栈中的存放的变量,多个线程之间是不能共享的。 |
下面根据图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线程终止。 |
从图4中可以清楚的看到,由于b线程对实例变量output的修改覆盖了a线程对实例变量output的修改,从而导致了用户a的信息显示在了用户b的浏览器上。如果在a线程执行输出语句时,b线程对output的修改还没有刷新到主存,那么将不会出以上所示的输出结果,因此这只是一种偶然现象,但这更增加了程序潜在的危险性。
由上面可以看出线程是不安全的。
解决方法
1.为每个请求创建一个servlet实例。每个实例之间不共享数据。
实现SingleThreadModel接口
public class ServletTest extends HttpServlet implements SingleThreadModel
2.读取数据的代码实现块同步,保证受保护的块一次只能一个线程访问。
synchronized (this)
{
response.setContentType ("text/html; charset=gb2312");
username = request.getParameter ("username");
output = response.getWriter ();
try
{
Thread.sleep (5000); //为了突出并发问题,在这设置一个延时
}
catch (InterruptedException e)
{
}
output.println("用户名:"+username+"<BR>");
}
3.把实例变量都定义在布局变量中(存放在堆栈上),防止多个线程之间共享变量。
public void service (HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter output;
String username;
response.setContentType ("text/html; charset=gb2312");
username = request.getParameter ("username");
output = response.getWriter ();
try
{
Thread.sleep (5000); //为了突出并发问题,在这设置一个延时
}
catch (InterruptedException e)
{
}
output.println("用户名:"+username+"<BR>");
}
- Servlet线程安全问题学习总结
- Servlet、Struts1 和Struts2 线程安全问题总结
- Servlet学习笔记—Servlet单例和线程安全问题
- servlet线程安全问题
- 关于servlet线程安全问题
- Servlet的线程安全问题
- servlet的线程安全问题
- servlet线程安全问题分析
- servlet线程安全问题
- Servlet 线程安全问题
- Servlet线程安全问题
- servlet线程安全问题分析
- servlet 线程安全问题
- Servlet的线程安全问题
- Servlet线程安全问题
- Servlet的线程安全问题
- Servlet的线程安全问题
- Servlet线程安全问题
- js版无刷新省市县三级联动
- 关于Java中的一些问题汇总
- 跨数据库Copy的一个类的代码
- 弹出窗口window.open()的参数列表
- IHttpHandler中捕获Session
- Servlet线程安全问题学习总结
- OpenLayers项目分析——(四)空间数据的组织与实现
- 浅谈工作流引擎的几个关键因素
- Twitter 概念与使用方法介绍
- 游戏中的AI编程(一) 遗传算法简介
- 查詢列名中有漢字的列
- 在C#中使用存储过程
- struts标签与HTML标签同JavaScript结合时的不同
- 简单的文件上传