Servlet线程不安全是如何体现的?

在这个遍地框架的年代,我相信很多人对于底层的Servlet的深入了解肯定很少,但是大家肯定对于Servlet的线程安全肯定有所涉猎,也都能讲个大概。我也和大家一样,在一次与同事之间

的闲聊时,谈到了Servlet(PS:现在的工作中应用的框架就只有spring,其他俩个框架没有用,用的是Servlet。)突然说到线程安全的问题,我不假思索的说句,Servlet之所以线程不安全,是

因为Servlet共享了一个实例变量,所以在多线程的环境下容易产生线程不安全的问题。但是同事却抛出了另外一个问题:Servlet线程不安全的确是因为多线程环境下共享一个实例变量导致

的,在你设计一个Servlet的时候应该如何去避免线程安全问题呢?也许很多同学会顺口的答出用锁,锁住这个实例变量就能保证在多线程环境下的线程安全问题。但现在的要求是不能锁住该实

例变量,你能办到吗?这个就是知其然不知其所以然,所以变换一种要求就会显得无所适从。

先从Servlet的工作原理说起:首先简单解释一下Servlet接收和响应客户请求的过程,首先客户发送一个请求,Servlet是调用service()方法对请求进行响应的,通过源代码可见,service()方

法中对请求的方式进行了匹配,选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没doGet,doPost

等等这些方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。

  每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方

法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定Servle

t的时候只需要继承HttpServlet即可。

  Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest,ServletResponse

强转为HttpRequest和HttpResponse。在Servlet整个生命周期中是由Tomcat来维护的,当客户端第一次发起请求的时候,会根据web.xml文件中的配置实例化一个Servlet,而在以后客户

端的每一次请求都会使用该实例来处理后续的工作,知道Tomcat停止该项目,这个Servlet才会被销毁,所占用的资源才会释放。

  当客户端发来多个请求的时候,Servlet将采用多线程来解决这样的并发,而在Tomcat本身也维护了一个线程池来处理并发。线程池实际上是等待执行代码的一组线程叫做工作组线程

(Worker Thread),Tomcat容器使用一个调度线程来管理工作组线程(Dispatcher Thead)。

              

我们还是来写个简单的Servlet实例来模拟下:

 1 import java.io.IOException;
 2 import java.io.PrintWriter;
 3
 4 import javax.servlet.http.HttpServlet;
 5 import javax.servlet.http.HttpServletRequest;
 6 import javax.servlet.http.HttpServletResponse;
 7
 8
 9 /**
10  * @author zWX240091
11  *
12  */
13 public class HelloWorldServlet extends HttpServlet
14 {
15     String message;
16     /**
17      *
18      */
19     private static final long serialVersionUID = 787553024399133588L;
20     public void service(HttpServletRequest request,HttpServletResponse response) throws IOException{
21         message =request.getParameter("message");
22         PrintWriter pw = response.getWriter();
23         try
24         {
25             Thread.sleep(5000);
26         }
27         catch (InterruptedException e)
28         {
29             e.printStackTrace();
30         }
31         pw.write("<div><strong>Hello World</strong>!</div>"+message);
32         pw.close();
33     }
34
35 }

在构建好的工程部署到本地Tomcat下,启动Tomcat,在打开俩个浏览器

分别访问1: http://localhost:8080/Servlet03/HelloWorld?message=helloA

分别访问2: http://localhost:8080/Servlet03/HelloWorld?message=helloB

然后分别刷新访问,然后你会惊喜的发现在访问第一个地址的页面打印出了helloB,在访问第二个地址的时候页面有时候会打印出helloA。这个就是高并发下的多线程的安全问题。

那如何不用加锁的情况下如何让高并发多线程的环境下线程安全呢?不知道大家注意到没有,在这个Servlet类里面,message这个属性是属于HelloWorldServlet这个实例变量,也就是说这

个实例变量是一个共享的实例变量,那么他所包含所有的属性都被这个实例变量共享了造成数据不匹配。我们是否可以将共享实例变量下的属性写到局部变量去呢?答案是可以的,将上面那段

代码中的属性message搬到Service中,在安全来的操作在访问下,你就会发现永远不会出现在传递过来的参数是helloA的时候页面会展示给你的是helloB,这样也就保证了能够线程安全了。

当然利用锁也是能够保证线程安全的,至于性能,就需要看你在什么样的环境去选择相应的方法去保证线程安全了。后面Google了下为什么局部变量为什么不会被共享,解释如下:多线程下每

个线程对局部变量都会有自己的一份copy,这样对局部变量的修改只会影响到自己的copy而不会对别的线程产生影响,线程安全的。但是对于实例变量来说,由于servlet在Tomcat中是以单

例模式存在的,所有的线程共享实例变量。多个线程对共享资源的访问就造成了线程不安全问题。

资料参考地址:深入理解Servlet线程安全问题http://blog.csdn.net/lcore/article/details/8974590

       Servlet生命周期与工作原理http://www.cnblogs.com/cuiliang/archive/2011/10/21/2220671.html

时间: 2024-10-12 16:19:29

Servlet线程不安全是如何体现的?的相关文章

javaweb回顾第六篇谈一谈Servlet线程安全问题

前言:前面说了很多关于Servlet的一些基础知识,这一篇主要说一下关于Servlet的线程安全问题. 1:多线程的Servlet模型 要想弄清Servlet线程安全我们必须先要明白Servlet实例是如何创建,它的模式是什么样的. 在默认的情况下Servlet容器对声明的Servlet,只创建一个Servlet实例,那么如果要是多个客户同时请求访问这个Servlet,Servlet容器就采取多线程.下面我们来看一幅图 从图中可以看出当客户发送请求的时候,Servlet容器通过调度者线程从线程池

(2.1)servlet线程安全问题

本文参考链接:http://www.yesky.com/334/1951334.shtml 摘 要:介绍了Servlet多线程机制,通过一个实例并结合Java 的内存模型说明引起Servlet线程不安全的原因,给出了保证Servlet线程安全的三种解决方案,并说明三种方案在实际开发中的取舍. Servlet/JSP技术和ASP.PHP等相比,由于其多线程运行而具有很高的执行效率.由于Servlet/JSP默认是以多线程模式执行的,所 以,在编写代码时需要非常细致地考虑多线程的安全性问题.然而,很

Servlet线程安全

public class servletDemo1 extends HttpServlet { int i=0; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { i++; System.out.println(i); } } public class servletDemo1 extends HttpServlet

过滤器、监听器、上下文、servlet线程安全问题

就像名字写那样,过滤器可以过滤请求,比如对一些评论进行过滤.又不改原有代码的基础上可以加上一个过滤器,或者是登陆验证.集中在一个过滤器中处理.写一个类实现接口 Filter 之后一定要在配置文件中配置!!!监听器可以监听,上下文的概念. 过滤器: 什么是过滤器: servlet规范当中定义的一种特殊的组件,用来拦截servlet容器的调用过程. 会先调过过滤器的方法,过滤器决定是否向后继续调用就是调用servlet容器 容器收到请求之后 通常情况下会调用servlet的service方法来处理请

Tomact高并发&amp;Servlet线程处理

Servlet/JSP技术和ASP.PHP等相比,由于其多线程运行而具有很高的执行效率.由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题.然而,很多人编写Servlet/JSP程序时并没有注意到多线程安全性的问题,这往往造成编写的程序在少量用户访问时没有任何问题,而在并发用户上升到一定值时,就会经常出现一些莫明其妙的问题. Servlet生命周期 1.装载Servlet.这项操作一般是动态执行的.然而,Server通常会提供一个管理的选项

servlet 线程安全问题的解决

一,servlet容器如何同时处理多个请求. Servlet采用多线程来处理多个请求同时访问,Servelet容器维护了一个线程池来服务请求. 线程池实际上是等待执行代码的一组线程叫做工作者线程(Worker Thread),Servlet容器使用一个调度线程来管理工作者线程(Dispatcher Thread). 当容器收到一个访问Servlet的请求,调度者线程从线程池中选出一个工作者线程,将请求传递给该线程,然后由该线程来执行Servlet的service方法. 当这个线程正在执行的时候,

servlet线程不安全

1.servlet是线程不安全的 局部变量不存在线程安全问题,比如: protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int i = 0; i++; try { java.lang.Thread.sleep(1000*10); } catch (InterruptedException e) { // TODO

servlet线程安全问题

由于默认情况下Servlet,在内存中只有一个实例对象,当多个浏览器并发访问Servlet时就有可能产生线程安全问题 解决方案: 1.加锁--效率降低 synchronized(this){  } 2.SingleThreadModel接口 -- 不能真的防止线程安全问题 在服务器的内部维护一个对象池,放servlet的对象,一个请求过来,会到池里检查一下有没有这个servlet,如果没有,创建一个给当前线程使用,使用完的就放回池里.如果第一个线程使用的servlet对象还没有还回池里,第二线程

JSP/Servlet线程安全

在进行servlet开发时,线程安全是很重要的,否则会导致一些意想不到的结果. Servlet的生命周期是由Web容器负责的,当客户端第一次请求Servlet时,容器负责初始化Servlet,也就是实例化这个Servlet类.以后这个实例就负责客户端的请求,一般不会再实例化这个Servlet类,也就是这个servlet实例被多个线程共享. 那么怎样才能是Servlet安全呢?答案是不要使用实例变量或类变量.当然你也可以使用synchronized同步方法或使用单线程模型,但这样效率不高. 临时变