避免servlet 在并发下线程安全问题
2015-09-09 14:36
579 查看
servlet是何物?
1,http://www.ibm.com/developerworks/cn/java/j-lo-servlet/
2,http://baike.baidu.com/link?url=2-VdfHr9F_FmPpe3Q3T_YNWRkUOAosodZpeTd-4qZHZwsHqg-w-hJ3fGM13Hkpe7Rrq1MTzjZrMznkgv80G_X_
由于 servlet 是单例的,所以所有对这个 servlet 的请求都是共享一个实例 因此导致成员变量 username 对于所有请求这个 servlet 的请求都是共享了,在多线程情况下就回导致 username 的值冲突,结果请看演示;用两个不同的浏览器(模拟两个线程)分别请求这个 servlet,并且输入不同的值,跳转之后两个show.jsp显示的值是后面点击提交的值;下面代码演示这个问题;
执行
1, 在浏览器 A(chrome)输入 :first
2,在浏览器 B(Safari)输入:second
执行的次序:先点击 chrome ,并且在10秒之内点击了 Safari ,等待执行结果。模拟并发访问。有兴趣的读者可以使用第三个浏览器访问,并且在10秒内分别提交;不出意外最后执行的结果应该是显示最后提交的那个数据。
页面执行结果:
后台打印的结果如下:
简单分析:
* 从结果可以判断出每个请求各自启动了一个线程处理请求;
* 当用户在浏览器 A(线程A) 提交 username 时候,执行到(
),把得到的值设置到 username 上,
* 此时休眠10秒,休眠的时候浏览器 B(线程B) 也提交了,在 A请求还没执到(
request.setAttribute("username",
username);
)期间 B 也执行了(
),
* 此时 username 的值就是 B 提交的值了;
* 然后 A 休眠完成,此时 username 的值已经不是 A 请求的值了!!
*
* 解决办法:
* 不把username 定义为成员变量,而是定义为方法内部的局部变量,每个线程的都各自有自己的方法区,自然方法内的变量是不可共享了!
* 此时 username 分别处在不同的线程,互不干扰,自然也就不会相互影响了!!
这是个很简单例子,但是很能说明问题了。在以后使用 servlet 的时候就要注意了!!!
由此延伸出另一个问题:struts2的 action 会不会也有这个问题呢????
答案是不会,因为 action 类是多例的,每个 request 请求都会实例化一个 action,每个实例是在各自的线程中的,自然也就不会出现线程的安全问题了……有兴趣的你可以验证看看或者看看官方文档即可!
原文地址:http://ibeginner.sinaapp.com/index.php?m=Home&c=Index&a=detail&id=d32fe7029e8841f475b9573611230881
1,http://www.ibm.com/developerworks/cn/java/j-lo-servlet/
2,http://baike.baidu.com/link?url=2-VdfHr9F_FmPpe3Q3T_YNWRkUOAosodZpeTd-4qZHZwsHqg-w-hJ3fGM13Hkpe7Rrq1MTzjZrMznkgv80G_X_
由于 servlet 是单例的,所以所有对这个 servlet 的请求都是共享一个实例 因此导致成员变量 username 对于所有请求这个 servlet 的请求都是共享了,在多线程情况下就回导致 username 的值冲突,结果请看演示;用两个不同的浏览器(模拟两个线程)分别请求这个 servlet,并且输入不同的值,跳转之后两个show.jsp显示的值是后面点击提交的值;下面代码演示这个问题;
package com.ubuntuvim.servlet.concurrent; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author ubuntuvim * @Email cdq@fortunes.com.cn * @2015年8月21日 上午1:54:09 */ public class ConcurrentServlet extends HttpServlet { private static final long serialVersionUID = 1L; // 成员变量,由于 servlet 是单例,所以每个 request 请求后的线程公用一个 servlet 实例 private String username; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 从页面获取 //String username = request.getParameter("username"); this.username = request.getParameter("username"); System.out.println("thread name = " + Thread.currentThread().getName()); System.out.println("username = " + this.username); // 模拟业务处理 try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } request.setAttribute("username", username); System.out.println("username >>> " + this.username); request.getRequestDispatcher("show.jsp").forward(request, response); } }
执行
1, 在浏览器 A(chrome)输入 :first
2,在浏览器 B(Safari)输入:second
执行的次序:先点击 chrome ,并且在10秒之内点击了 Safari ,等待执行结果。模拟并发访问。有兴趣的读者可以使用第三个浏览器访问,并且在10秒内分别提交;不出意外最后执行的结果应该是显示最后提交的那个数据。
页面执行结果:
后台打印的结果如下:
简单分析:
* 从结果可以判断出每个请求各自启动了一个线程处理请求;
* 当用户在浏览器 A(线程A) 提交 username 时候,执行到(
1 | this .username = request.getParameter( "username" ); |
* 此时休眠10秒,休眠的时候浏览器 B(线程B) 也提交了,在 A请求还没执到(
request.setAttribute("username",
username);
)期间 B 也执行了(
1 | request.getParameter( "username" ) |
* 此时 username 的值就是 B 提交的值了;
* 然后 A 休眠完成,此时 username 的值已经不是 A 请求的值了!!
*
* 解决办法:
* 不把username 定义为成员变量,而是定义为方法内部的局部变量,每个线程的都各自有自己的方法区,自然方法内的变量是不可共享了!
* 此时 username 分别处在不同的线程,互不干扰,自然也就不会相互影响了!!
这是个很简单例子,但是很能说明问题了。在以后使用 servlet 的时候就要注意了!!!
由此延伸出另一个问题:struts2的 action 会不会也有这个问题呢????
答案是不会,因为 action 类是多例的,每个 request 请求都会实例化一个 action,每个实例是在各自的线程中的,自然也就不会出现线程的安全问题了……有兴趣的你可以验证看看或者看看官方文档即可!
原文地址:http://ibeginner.sinaapp.com/index.php?m=Home&c=Index&a=detail&id=d32fe7029e8841f475b9573611230881
相关文章推荐
- Android 技术总结(004)—— ScrollView中的组件设置android:layout_height="fill_parent"不起作用的解决办法
- javascript 原生 模仿 jquery 选择器 代码
- PWM波(存在问题,待解决)
- 计蒜客 第9题:元素移除
- 批处理定时自动更新SVN
- 通过SqlParameter防止注入式攻击
- 字符串匹配的KMP算法(转载)
- 数据处理:离散化好处多
- Java 的 interface、abstract class 与 C++ 的多继承、虚基类
- POJ 2337 - Catenyms(欧拉回路)
- Spark入门实战系列--2.Spark编译与部署(下)--Spark编译安装
- Java中ArrayList和LinkedList区别
- 游标的应用,一个小小的功能,做一下记录
- JQuery给元素绑定click事件多次执行的解决方法
- 用存储过程判断某个人员在一天的行进轨迹中是否有超过指定时间的停留
- Hbase中的BloomFilter(布隆过滤器)
- 七、cocos2d热更新
- Amazon S3 云存储服务简介
- CentOS6.6下bind搭建DNS服务器
- 安卓view.getMeasuredHeight()得到的高度单位以及dp和px的转换