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

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值