(未完)7-Servlet线程安全问题

一、ServletContext、HttpSession是线程安全的;ServletRequest是非线程安全的

参考:https://www.cnblogs.com/digdeep/p/4429098.html

1、保存在ServletContext、HttpSession中的对象必须是线程安全的;

在Java web项目中,我们经常要将一个登陆的用户保存在HttpSession中,而这个User对象就是像下面定义的一样的一个Java bean。

为什么将一个这样的Java对象保存在HttpSession中是有问题的,至少在线程安全方面不严谨的,可能会出现并发问题。

Tomcat8.0中HttpSession的源码在org.apache.catalina.session.StandardSession.java文件中,源码分析如下(截取我们需要的部分):

我们看到每一个独立的HttpSession中保存的所有属性,是存储在一个独立的ConcurrentHashMap中的:

protected Map attributes = new ConcurrentHashMap<>();

所以我可以看到 HttpSession.getAttribute(), HttpSession.setAttribute() 等等方法就都是线程安全的。

另外如果我们要将一个对象保存在HttpSession中时,那么该对象应该是可序列化的。不然在进行HttpSession的持久化时,就会被抛弃了,无法恢复了:

            else if ( (value instanceof Serializable)

                    && (!exclude(keys[i]) )) {

                saveNames.add(keys[i]);

                saveValues.add(value);

            } else {

                removeAttributeInternal(keys[i], true);

            }

所以从源码的分析,我们得出了下面的结论:

1)HttpSession.getAttribute(), HttpSession.setAttribute() 等等方法都是线程安全的;

2)要保存在HttpSession中对象应该是序列化的;

虽然getAttribute,setAttribute是线程安全的了,那么下面的代码就是线程安全的吗?

session.setAttribute("user", user);

User user = (User)session.getAttribute("user", user);

不是线程安全的!因为User对象不是线程安全的假如有一个线程执行下面的操作:

User user = (User)session.getAttribute("user", user);

user.setName("xxx");

那么显然就会存在并发问题。因为会出现:有多个线程访问同一个对象 user, 并且至少有一个线程在修改该对象。但是在通常情况下,我们的Java web程序都是这么写的,为什么又没有出现问题呢?原因是:在web中 ”多个线程访问同一个对象 user, 并且至少有一个线程在修改该对象“ 这样的情况极少出现;因为我们使用HttpSession的目的是在内存中暂时保存信息,便于快速访问,所以我们一般不会进行下面的操作:

User user = (User)session.getAttribute("user", user);

user.setName("xxx");

我们一般是只使用对从HttpSession中的对象使用get方法来获得信息,一般不会对”从HttpSession中获得的对象“调用set方法来修改它;而是直接调用 setAttribute来进行设置或者替换成一个新的。

3. 结论

所以结论是:如果你能保证不会对”从HttpSession中获得的对象“调用set方法来修改它,那么保存在HttpSession中的对象可以不是线程安全的(因为他是”事实不可变对象“,并且ConcurrentHashMap保证了它是被”安全发布的“);但是如果你不能保证这一点,那么你必须要实现”保存在HttpSession中的对象必须是线程安全“。不然的话,就存在并发问题。

使Java bean线程安全的最简单方法,就是在所有的get/set方法都加上synchronized。 

2、ServletRequest是非线程安全的 

先区分一下:

  • HttpServletRequest和ServletRequest都是接口
  • HttpServletRequest继承自ServletRequest
  • HttpServletRequest比ServletRequest多了一些针对于Http协议的方法。如getHeader (String name), getMethod ()getSession () 等等。

他们对应的实现类:

javax.servlet.ServletRequestWrapper (implements javax.servlet.ServletRequest)

javax.servlet.http.HttpServletRequestWrapper (implements javax.servlet.http.HttpServletRequest) 

 

 

你可能感兴趣的:(java面试,简历知识点)