标签:
转自:http://www.cnblogs.com/yjhrem/articles/3160864.html
Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例); 2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的
设置线程池中线程数目,初始化线程池通过web.xml,初始化每个参数值等等。 3.当请求到达时,Servlet容器通过调度线程(Dispatchaer Thread) 调度它管理下线程池中等待执行的线程(Worker Thread)给请求者; 4.线程执行Servlet的service方法; 5.请求结束,放回线程池,等待被调用; (注意:避免使用实例变量(成员变量),因为如果存在成员变量,可能发生多线程同时访问该资源时,都来操作它,照成数据的不一致,因此产生线程安全问题) 从上面可以看出: 第一:Servlet单实例,减少了产生servlet的开销; 第二:通过线程池来响应多个请求,提高了请求的响应时间; 第三:Servlet容器并不关心到达的Servlet请求访问的是否是同一个Servlet还是另一个Servlet,直接分配给它一个新的线程;如果是同一个Servlet的多个请求,那么Servlet的service方法将在多线程中并发的执行; 第四:每一个请求由ServletRequest对象来接受请求,由ServletResponse对象来响应该请求; Servlet/JSP技术和ASP、PHP等相比,由于其多线程运行而具有很高的执行效率。由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题。 JSP的中存在的多线程问题: 当客户端第一次请求某一个JSP文件时,服务端把该JSP编译成一个CLASS文件,并创建一个该类的实例,然后创建一个线程处理CLIENT端的请求。如果有多个客户端同时请求该JSP文件,则服务端会创建多个线程。每个客户端请求对应一个线程。以多线程方式执行可大大降低对系统的资源需求,提高系统的并发量及响应时间. 对JSP中可能用的的变量说明如下: 实例变量: 实例变量是在堆中分配的,并被属于该实例的所有线程共享,所以不是线程安全的. JSP系统提供的8个类变量 JSP中用到的OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT是线程安全的(因为每个线程对应的request,respone对象都是不一样的,不存在共享问题), APPLICATION在整个系统内被使用,所以不是线程安全的. 局部变量: 局部变量在堆栈中分配,因为每个线程都有它自己的堆栈空间,所以是线程安全的. 静态类: 静态类不用被实例化,就可直接使用,也不是线程安全的. 外部资源: 在程序中可能会有多个线程或进程同时操作同一个资源(如:多个线程或进程同时对一个文件进行写操作).此时也要注意同步问题. 使它以单线程方式执行,这时,仍然只有一个实例,所有客户端的请求以串行方式执行。这样会降低系统的性能 问题 问题一. 说明其Servlet容器如何采用单实例多线程的方式来处理请求 问题二. 如何在开发中保证servlet是单实例多线程的方式来工作(也就是说如何开发线程安全的servelt)。 一. Servlet容器如何同时来处理多个请求 Java的内存模型JMM(Java Memory Model) JMM主要是为了规定了线程和内存之间的一些关系。根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有实例变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存由缓存和堆栈两部分组成,缓存中保存的是主存中变量的拷贝,缓存可能并不总和主存同步,也就是缓存中变量的修改可能没有立刻写到主存中;堆栈中保存的是线程的局部变量,线程之间无法相互直接访问堆栈中的变量。根据JMM,我们可以将论文中所讨论的Servlet实例的内存模型抽象为图所示的模型。 工作者线程Work Thread:执行代码的一组线程。 调度线程Dispatcher Thread:每个线程都具有分配给它的线程优先级,线程是根据优先级调度执行的。 Servlet采用多线程来处理多个请求同时访问。servlet依赖于一个线程池来服务请求。线程池实际上是一系列的工作者线程集合。Servlet使用一个调度线程来管理工作者线程。 当容器收到一个Servlet请求,调度线程从线程池中选出一个工作者线程,将请求传递给该工作者线程,然后由该线程来执行Servlet的service方法。当这个线程正在执行的时候,容器收到另外一个请求,调度线程同样从线程池中选出另一个工作者线程来服务新的请求,容器并不关心这个请求是否访问的是同一个Servlet.当容器同时收到对同一个Servlet的多个请求的时候,那么这个Servlet的service()方法将在多线程中并发执行。 Servlet容器默认采用单实例多线程的方式来处理请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间,对于Tomcat可以在server.xml中通过元素设置线程池中线程的数目。 就实现来说: 调度者线程类所担负的责任如其名字,该类的责任是调度线程,只需要利用自己的属性完成自己的责任。所以该类是承担了责任的,并且该类的责任又集中到唯一的单体对象中。而其他对象又依赖于该特定对象所承担的责任,我们就需要得到该特定对象。那该类就是一个单例模式的实现了。 注意:服务器可以使用多个实例来处理请求,代替单个实例的请求排队带来的效益问题。服务器创建一个Servlet类的多个Servlet实例组成的实例池,对于每个请求分配Servlet实例进行响应处理,之后放回到实例池中等待下此请求。这样就造成并发访问的问题。 此时,局部变量(字段)也是安全的,但对于全局变量和共享数据是不安全的,需要进行同步处理。而对于这样多实例的情况SingleThreadModel接口并不能解决并发访问问题。 SingleThreadModel接口在servlet规范中已经被废弃了。 二 如何开发线程安全的Servlet 1、实现 SingleThreadModel 接口 该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。这种方法只要将前面的Concurrent Test类的类头定义更改为: Public class Concurrent Test extends HttpServlet implements SingleThreadModel { ………… } 2、同步对共享数据的操作 使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,在本论文中的Servlet可以通过同步块操作来保证线程的安全。同步后的代码如下: ………… Public class Concurrent Test extends HttpServlet { ………… Username = request.getParameter ("username"); Synchronized (this){ Output = response.getWriter (); Try { Thread. Sleep (5000); } Catch (Interrupted Exception e){} output.println("用户名:"+Username+"标签:
原文地址:http://my.oschina.net/liangzhenghui/blog/506141