您的位置:首页 > 其它

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

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"
);
),把得到的值设置到 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: