【转】多用户访问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. public Test() {
  5. super();
  6. count ++;
  7. System.out.println("实例化了 "+count+" 次");
  8. }
  9. public void doGet(HttpServletRequest request, HttpServletResponse response)
  10. throws ServletException, IOException {
  11. response.setContentType("text/html");
  12. num ++;
  13. System.out.println("被访问了" + num+"次");
  14. }
  15. }

打开浏览器,访问指定的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. public Test() {
  4. super();
  5. }
  6. public void doGet(HttpServletRequest request, HttpServletResponse response)
  7. throws ServletException, IOException {
  8. response.setContentType("text/html");
  9. out = response.getWriter();
  10. out.println(request.getRemoteAddr());
  11. }
  12. }

会有什么结果?单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. <!--number 为临界区-->
  5. <%! int number=0;
  6. synchronized void countPeople()
  7. { number++;
  8. }
  9. %>
  10. <% countPeople();
  11. %>
  12. <P><P>您是第
  13. <%=number%>
  14. 个访问本站的客户。
  15. </BODY>
  16. </HTML>

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

时间: 2024-10-01 03:31:19

【转】多用户访问Servlet,servlet单实例多线程的相关文章

【转】怎样理解Servlet的单实例多线程

转自 http://blog.chinaunix.net/uid-7374279-id-3687149.html 首先明确:Servlet是单实例的,即对于同一种业务请求只有一个是实例.不同的业务请求可以通过分发来产生多个实例.其次:单实例的原因我想是因为单实例足可以处理某一个请求,就像ibatis的Querydao.UpdateDao一样都是单实例的.再次:为什么单实例足可以处理某一个请求,因为Servlet是单实例多线程的.http://hiyachen.cublog.cn  [email 

对Servlet单实例多线程的理解。

0 Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: Servlet是单实例多线程运行方式,所以对象变量线程不安全,局部变量线程安全的. 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例): 2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的<Connector>设置线程池中线程数目,初始化线程池通过web.xml,初始

Servlet单实例多线程及注意事项。

1.Servlet是单实例多线程的,具体是怎么实施这个过程的可以百度,前辈们已经很具体详细的写明白了. 2.Servlet的单实例指的是这个servlet类只被实例化一次,就是第一次请求这个servlet的时候 3.Servlet怎么处理一个请求: ①你编写了一个servlet类 ②请求到了,需要servlet去处理,这个时候被类被实例化一次(成员变量在堆中开辟内存,临时变量在栈中开辟内存),得到了一个servlet实例,它的处理业务的关键性代码和你写的一样. ③开始处理请求:先把刚才实例化的s

servlet单实例多线程模式

前言:Servlet/JSP技术和ASP.PHP等相比,由于其多线程运行而具有很高的执行效率.由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题. JSP的中存在的多线程问题: 当客户端第一次请求某一个JSP文件时,服务端把该JSP编译成一个CLASS文件,并创建一个该类的实例,然后创建一个线程处理CLIENT端的请求.如果有多个客户端同时请求该JSP文件,则服务端会创建多个线程.每个客户端请求对应一个线程.以多线程方式执行可大大降低对系统

(转)sevlet单实例多线程模式

前言:Servlet/JSP技术和ASP.PHP等相比,由于其多线程运行而具有很高的执行效率.由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题. JSP的中存在的多线程问题: 当客户端第一次请求某一个JSP文件时,服务端把该JSP编译成一个CLASS文件,并创建一个 该类的实例,然后创建一个线程处理CLIENT端的请求.如果有多个客户端同时请求该JSP文件,则服务端会创建多个线程.每个客户端请求对应一个线程. 以多线程方式执行可大大降低对

[转]Servlet 单例多线程

Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例): 2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的<Connector>设置线程池中线程数目,初始化线程池通过web.xml,初始化每个参数值等等. 3.当请求到达时,Servlet容器通过调度线程(Dispatcha

Servlet单例多线程

Servlet如何处理多个请求访问?Servlet容器默认是采用单实例多线程的方式处理多个请求的:1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例):2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的<Connector>设置线程池中线程数目,初始化线程池通过web.xml,初始化每个参数值等等.3.当请求到达时,Servlet容器通过调度线程(Dispatchaer T

Servlet 单例多线程

转自:http://www.cnblogs.com/yjhrem/articles/3160864.html Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例): 2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的 设置线程池中线程数目,初始化线程池通过web.xml,初始

Servlet 单例多线程【转】

源地址:Servlet 单例多线程 Servlet如何处理多个请求访问?Servlet容器默认是采用单实例多线程的方式处理多个请求的:1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例):2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的<Connector>设置线程池中线程数目,初始化线程池通过web.xml,初始化每个参数值等等.3.当请求到达时,Servlet容器通过