关于Servlet线程安全的问题
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,和局部变量一样。