请教一下,关于Servlet的线程安全的问题

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:
谢谢你的回复。我会去看的。

你可以用 FF 看一下 cookie ,里边有一个 jsessionid 的值。你在服务器端用 request.getSession() 取 session 的时候,实际上就是用这个 id 去拿 session 对象。你可以想象在 server 端有个很大的 hash map ,里边的 key 就是这个 id ,value 就是 session 对象。至于你说你在 FF 里,两个 tab 取到的是同一个 session ,这是当然的,因为你在同一个浏览器进程里,对于两个 tab ,他们对应的 jsessionid 是一样的,你可以查看一下。

对于两个真正的用户,不可能用同一个机器的同一个浏览器,所以他们不可能用到同一个 jsessionid ,固此不会出现冲突。

如果你用两个不同的浏览器测试,就不会出现这个问题。

不需要,你的 session 对象是从 request 中取出的,request 对象对于不同的请求来说,是肯定不会冲突的。

对了,javaeye 不是用 Java 写的,所以他 cookie 里没有 jsessionid ,但是肯定有一个类似的值对应 session id 。

我觉得你对 servlet 规范、servlet 线程模型 概念不清楚,建议你仔细研究一下规范。简单地说,你那样的写法完全没必要,在你的页面里不需要用 synchronized 。更多的这里也很难解释清楚了。