关于Servlet线程安全的问题

Bellboy 2020-02-02

1、多线程Servlet模型--单实例多线程的模式

默认情况下servlet对声明的servlet,只创建一个servlet实例,多个客户访问这个servlet那么servlet容器采取多线程方式。

多个客户同事请求同一个servlet,那么会有多个线程同时执行这个servlet实例的service方法,servlet容器采取的就是单实例多线程的模式,节省了servlet实例的创建,但是引发的并发问题。

2、servlet的线程安全

2.1变量的线程安全

2.1.1避免全局变量

public class HelloWorldServlet extends HttpServlet{
    private String userName;    
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
    {
        userName=request.getParameter("userName");
        PrintWriter out=response.getWriter();
        if(userName!=null&&userName!="")
        {
            out.print(userName);
        }
        else {
            out.println("用户名不存在");
        }
    }
}

上面的userName为全局变量,多线程调用时会有问题,将变量生命正在方法内(局部变量),每个线程执行方法时重新实例化userName,这样就不会有问题了。

如果是静态资源可以添加final修饰,表示不可变

2.2属性的安全

servlet中可以访问保存在servletContent,httpSession,ServletRequest对象中的属性,这三种对象都提供了getAttribute(),setAttribute()方法用来设置获取属性值,那么这三个对象是否是线程安全?

2.2.1servletContent

首先servletContent是被应用程序下所有的servlet共享的,多个servlet对servletContent同时进行设置和访问,就会出现线程并发问题。

//代码一 
protected void service(HttpServletRequest request, HttpServletResponse response)
    {
        String userName=request.getParameter("userName");
        if ("login") {
            List list=(List)getServletContext().getAttribute("userList");
            list.add(userName);
        }
        else {
            List list=(List)getServletContext().getAttribute("userList");
            list.remove(userName);
        }
    }
//代码二
protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
    {
        List list=(List)getServletContext().getAttribute("userList");
        int count=list.size();
        for(int i=0;i<count;i++)
        {
            PrintWriter out=response.getWriter();
            out.println(list.get(i));
        }
    }

两个请求并发,当线程一执行到代码二第5行时,此时count=5,但是线程二此时执行代码一第10行,删除了一个数据,当线程一再执行就会出现数组越界的问题,怎么解决此类问题呢,第一就是把servletContent拷贝保存起来,第二种就是使用synchronized进行同步(效率低)

2.2.2  httpSession

httpSession是在用户会话中存在的,不像ServletContent是所有的用户共享的,所有说一个httpSession同一时刻只能有同一个用户进行请求,理论上看起来是线程安全的,其实并不是,这和浏览器有关系,同一个浏览器只能具有一个session,如果同一个用户在两个浏览器窗口同时请求,同样会出现线程安全的问题。

2.2.3httpRequest

httpRequest是线程安全的,应为每个线程都会调用service,都会创建一个新的httpRequest,和局部变量一样。

3:SingleThreadModel
从名字很好理解,就是单线程模式,也就是说如果Servlet实现了SingleThreadModel接口,Servlet容器就保证一个时刻只有一个线程在Servlet实例的Service方法运行(其实和同步差不多)这样一来就很影响效率了,现在SingleThreadModel已经被废弃了,值得注意的是就算Servlet实现了SingleThreadModel接口并不一定保证线程安全,比喻上面说的ServletContext,HttpSession,因为ServletContext是应用程序共享的,可能2个Servlet实例同时运行造成线程安全,HttpSession因为是在同一浏览器共享的所以也会出现(虽然可能性很小)
4:总结
1:只要我们了解Servlet容器工作的模式,可能就能够理解为什么Servlet会出现线程安全问题,所以一定牢记Servlet容器是多线程单实例的模型
2:避免使用全局变量,最好是使用局部变量,其实这本身也是一个好的编程习惯
3:应该使用只读的实例变量和静态变量(就是前面加上final意为不可改变)
4:不要在Servlet上自己创建线程,因为Servlet容器已经帮我们做好了。
5:如果要修改共享对象的时候记得要同步,尽量缩小同步的范围(比喻修改Session时候直接使用synchronized(Session)即可),避免影响性能

相关推荐