Servlet是线程不安全的,因为默认情况下是只实例化一个实例出来,然后,为每一个请求分配一个线程来运行,我看到有些书中说,可以把访问共享数据的代码同步下,就可以实现线程安全了,但是我对此有疑问。会话不是线程安全的,因为如果我们同时打开两个浏览器窗口,那么实际上就是两个请求,两个线程,两个线程访问同一个会话,就有线程安全问题,比如我们对一个会话设置属性:
...
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{
...
Session session = request.getSession();
String username = request.getParameter("username");
synchronized(session) {
session.setAttribute("username", username);
}
...
}
...
我们在jsp页面用到这个"username"属性:
<%
String username = session.getAttribute("username");
%>
Welcome <%= username %> !
那么我们是否要对上面的代码也同步呢? 变成这样:
<%
String username = null;
synchronized(session) {
username = (String)session.getAttribute("username");
}
%>
Welcome <%= username %> !
这样就能实现线程安全了吗?
虽然我们对访问会话的代码进行了同步,在同一时刻,只能有一个线程对会话访问,但是,是否会出现这种情况呢?
有两个线程A,B,线程A先运行,B后运行
线程A-->锁定会话-->访问会话,设置属性username值为"aaa"-->解锁-->进入jsp-->锁定会话-->访问会话的username属性,这时的username属性被线程B覆盖了,所以得到"bbb"-->解锁-->继续执行其他指令。
线程B-->等待解锁-->锁定同一会话,设置username属性值为"bbb"-->解锁-->进入jsp-->锁定会话-->访问会话的username属性得到"bbb"-->解锁-->继续执行其他指令。
简单说,就是在线程A解锁了session之后,线程B马上就进入了session,覆盖了线程A的username属性为"bbb",然后线程A在jsp页面里得到就是"bbb",而非"aaa",我这样理解对不对呢?
请明白的朋友不吝赐教,谢谢!
[b]问题补充:[/b]
我不知道怎么回复我的问题,所以只好用问题补充来作为回帖了。
回复alexma:
我觉得对每一个不同的请求,request对象是不同的,但是用这个方法:
request.getSession();
并非是每次都创建一个新的session对象,如果已经有一个session对象存在,并且客户端浏览器支持cookie的话,取出来就是原来session对象的引用,那么是有可能两个request对象获得同一个session对象的引用,比如,我在自己的机器上打开FireFox3访问JavaEye论坛,然后又打开一个tab页面,也访问JavaEye,这种情况下,两个不同的request,却会与同一个session对象联系。
[b]问题补充:[/b]
回复alexma:
你说的我理解,但是我的意思是说,有两个[b]线程[/b],非进程,与同一个session对象进行联系的话,像我上面那种写法(同步对session进行操作的代码),是否仍然存在线程安全问题?不是讨论这样是否能模拟让两个用户出现冲突的情况,而是讨论的是线程安全的问题。
谢谢你的回复。
[b]问题补充:[/b]
回复alexma:
谢谢你的回复。我会去看的。