避免servlet 在并发下线程安全问题

来源:互联网 发布:淘宝卖家详情页尺寸 编辑:程序博客网 时间:2024/06/06 08:25

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显示的值是后面点击提交的值;下面代码演示这个问题;

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

blob.png

2,在浏览器 B(Safari)输入:second

blob.png

执行的次序:先点击 chrome ,并且在10秒之内点击了 Safari ,等待执行结果。模拟并发访问。有兴趣的读者可以使用第三个浏览器访问,并且在10秒内分别提交;不出意外最后执行的结果应该是显示最后提交的那个数据。


页面执行结果:

blob.png

后台打印的结果如下:

blob.png

简单分析:

 * 从结果可以判断出每个请求各自启动了一个线程处理请求;

 * 当用户在浏览器 A(线程A) 提交 username 时候,执行到(

1this.username = request.getParameter("username");

),把得到的值设置到 username 上,

 * 此时休眠10秒,休眠的时候浏览器 B(线程B) 也提交了,在 A请求还没执到(

request.setAttribute("username", username);

)期间 B 也执行了(

1request.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

0 0
原创粉丝点击