zoukankan      html  css  js  c++  java
  • 【转】多用户访问Servlet,servlet单实例多线程

    1. public class MyClass {  
    2.     private String variable1 ;  
    3.     private static String variable2 ;  
    4.     public MyClass(){     
    5.     }  
    6.     public void method(){  
    7.         String variable3;  
    8.     }  
    9.  }  

    上面是随手写的一个类,没有任何意义,只是为了强调一些概念,这和这个主题很有关系:
    1) java中的变量的分类:
        a. 实例变量
        b. 局部变量
        c. 静态变量
    本篇并不是java的基础教程,因此不会详尽到每个基础知识点(下面的内容是区分对象和对象变量这两个概念的,<<core java2>>严格区分,不过大多数教材并不过于苛刻的区别它们)
    a. 实例变量:属于每个对象,也就是每个对象都有一份此变量的副本。上面的variable1就指向实例变量。
    b. 局部变量:工作在某个作用域,离开作用域之后成为垃圾。上面的variable3就指向局部变量,局部变量存在于方法中。
    c. 静态变量:属于某个类,也就是所有对象共有一个副本。上面的variable2就指向静态变量。

    2) servlet容器的实例化:
    servlet如何被实例化的,不是我们所关心的,我们关心的是servlet被实例化了多少次,是每一次请求都实例化还是仅仅实例化一次?要解答这个问题太简单了:

    [java] view plaincopy
     
    1. public class Test extends HttpServlet {  
    2.     private static int count = 0;  
    3.     private int num = 0;  
    4.      
    5.     public Test() {  
    6.         super();  
    7.         count ++;  
    8.         System.out.println("实例化了 "+count+" 次");  
    9.     }  
    10.   
    11.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
    12.             throws ServletException, IOException {  
    13.         response.setContentType("text/html");  
    14.         num ++;  
    15.         System.out.println("被访问了" + num+"次");  
    16.     }  
    17. }  



    打开浏览器,访问指定的servlet,不断点刷新,结果是:
    实例化了 1 次
    被访问了1次
    被访问了2次
    被访问了3次

    不要关闭浏览器,再开一个浏览器访问,不断点刷新,结果是:
    被访问了4次
    被访问了5次
    被访问了6次
    被访问了7次
    被访问了8次

    可见对不仅仅是对同一个用户,对于其他用户也只实例化一个对象。
    也就是说,Tomcat仅仅实例化一次servlet,产生一个对象。

    那servlet容器是如何相应多用户同时访问的呢?回答:多线程。为每次访问都用一个独立的线程运行doGet或者是doPost方法。也就是servlet容器的内部实现可能是这样的:

    [java] view plaincopy
     
    1. class . extends Thread{  
    2.     public void run(){  
    3.        if(request 为 get)  
    4.            servlet.doGet();   // servlet指向你请求的servlet类的实例  
    5.        else if(request 为 post)  
    6.            servlet.doPost();  
    7.     }  
    8. }  



    这没什么问题,但是一些初级程序员可能放这样的错误:

    [java] view plaincopy
     
    1. public class Test extends HttpServlet {  
    2.     private PrintWriter out ;  
    3.   
    4.     public Test() {  
    5.         super();  
    6.     }  
    7.   
    8.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
    9.             throws ServletException, IOException {  
    10.         response.setContentType("text/html");  
    11.        out = response.getWriter();  
    12.        out.println(request.getRemoteAddr());  
    13.     }  
    14. }  



    会有什么结果?单A访问这个Test,通过response.getWriter()得到一个实例,保存在out中,假定这时候B在A之前执行完了response.getWriter(),并开始执行out.println(request.getRemoteAddr()),这时候out保存的并不是B自己通过getWriter()方法得到的PrintWriter对象,而是A运行getWriter的结果,那么B就输出了A的地址。再看看下面的例子:

    建立一个webapp,名字叫做web,建立sendname.html的HTML文件,内容如下:

    [java] view plaincopy
     
    1. <html>  
    2.   <head></head>  
    3.   <body>  
    4.         <form action="/web/Test">  
    5.             <input type="text" name="name"/>  
    6.             <input type="submit" />  
    7.         </form>  
    8.   </body>  
    9. </html>  



    建立一个servlet叫Test,内容如下:

    [java] view plaincopy
     
    1. public class Test extends HttpServlet {  
    2.     private String name ;  
    3.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
    4.             throws ServletException, IOException {  
    5.         response.setContentType("text/html");  
    6.         PrintWriter out = response.getWriter();  
    7.         name = request.getParameter("name");  
    8.         out.println("I input "+name);  
    9.         try{Thread.sleep(5000);}catch(Exception ex){}  
    10.         out.println("I am "+name);  
    11.         out.flush();  
    12.         out.close();  
    13.     }  
    14. }  



    通过浏览器打开2个sendname.html(http://localhost:8080/web/sendname.html),分别输入名字A和B(间隔不要超过5秒):
    结果2个页面显示分别是:

    I input B I am B

    I input A I am B

    这就是Servlet中的多线程问题。解决的办法很简单,就是不用实例变量,至少不在绝对必要的时候用。
    在JSP中这样的问题更加突出,因为使用<%!  %>进行的变量声明得到的是一个实例变量,如果一定要定义,那么就使用synchronized,在使用实例变量的方法前加上synchronized,它也是不推荐使用的,下面是一个例子:

    [java] view plaincopy
     
    1. <%@ page contentType="text/html;charset=GB2312" %>  
    2. <HTML>  
    3. <BODY>  
    4.   
    5.     <!--number 为临界区-->  
    6.     <%! int number=0;      
    7.      synchronized void countPeople()  
    8.         { number++;  
    9.         }  
    10.     %>  
    11.     <% countPeople();  
    12.     %>  
    13. <P><P>您是第  
    14.    <%=number%>  
    15. 个访问本站的客户。  
    16. </BODY>  
    17. </HTML>  


    在<%! %>中定义的方式也会被多线程调用。
    对于JSP页面,除了使用synchronized,还可以使用page指令元素的isThreadSafe来控制整个页面是否可以多线程访问。

  • 相关阅读:
    Harbor
    Ansible自动化部署详细教程
    企业级-Shell案例18——目录入侵检测与告警
    企业级-Shell案例17——DOS攻击防范(自动屏蔽攻击IP)
    企业级-Shell案例16——自动发布PHP项目
    springboot启动报错,Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
    IO流05--毕向东JAVA基础教程视频学习笔记
    IO流04--毕向东JAVA基础教程视频学习笔记
    IO流03--毕向东JAVA基础教程视频学习笔记
    IO流02--毕向东JAVA基础教程视频学习笔记
  • 原文地址:https://www.cnblogs.com/specification/p/4558642.html
Copyright © 2011-2022 走看看