Servlet的模板方法和多线程

接触了一小段时间的servlet,下面就总结一下关于servlet的两个方面的知识,一个是模板方法的应用。另外一个是servlet多线程产生的原因。

1. 模板方法设计模式

定义一个操作中的算法的骨架,而将步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。

2. Servlet中模板方法的应用

模板方法在servlet中的应用的类图

当客户端请求一个servlet的时候首先被调用的是service方法。Service方法就是定义骨架的方法,在service方法中会根据HTTP请求的类型(GET,POST还是其他)来调用具体的doGet和doPost等方法;即实际的处理委派给了doGet和doPost等方法,这些子类的方法来最终处理浏览器的请求。下面是HttpServlet对service方法的具体实现。

protected void service(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	String method = req.getMethod();

	if (method.equals(METHOD_GET)) {
	    long lastModified = getLastModified(req);
	    if (lastModified == -1) {
		// servlet doesn't support if-modified-since, no reason
		// to go through further expensive logic
		doGet(req, resp);
	    } else {
		long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
		if (ifModifiedSince < (lastModified / 1000 * 1000)) {
		    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
		    maybeSetLastModified(resp, lastModified);
		    doGet(req, resp);
		} else {
		    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
		}
	    }

	} else if (method.equals(METHOD_HEAD)) {
	    long lastModified = getLastModified(req);
	    maybeSetLastModified(resp, lastModified);
	    doHead(req, resp);

	} else if (method.equals(METHOD_POST)) {
	    doPost(req, resp);

	} else if (method.equals(METHOD_PUT)) {
	    doPut(req, resp);	

	} else if (method.equals(METHOD_DELETE)) {
	    doDelete(req, resp);

	} else if (method.equals(METHOD_OPTIONS)) {
	    doOptions(req,resp);

	} else if (method.equals(METHOD_TRACE)) {
	    doTrace(req,resp);

	} else {
	    //
	    // Note that this means NO servlet supports whatever
	    // method was requested, anywhere on this server.
	    //

	    String errMsg = lStrings.getString("http.method_not_implemented");
	    Object[] errArgs = new Object[1];
	    errArgs[0] = method;
	    errMsg = MessageFormat.format(errMsg, errArgs);

	    resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
	}
    }

3. servlet并不是严格的按照模板方法来设计的

我们常说具体问题具体分析,在这里也是。servlet 讲模板方法的精髓应用到了,但是却没有完全按照模板方法设计。

3.1 子类可以重写service()方法

模板方法模式是通过把不变的行为搬移到超类,去除了子类中的重复代码。所以超类中的模板方法一般情况下是不会被重写的。模板方法允许子类重新定义算法的某些     步骤,而不改变算法的结构。但是在servlet中的service方法中是可以被子类重写的。当子类重写了service方法之后就破坏掉了原本定义好的模板方法。

3.2 doGet和doPost方法有默认的实现

在HttpServlet类中对于这两个被子类具体实现的方式是有实现的。下面是HttpServlet对于这两个实现的代码

 protected void doPost(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	String protocol = req.getProtocol();
	String msg = lStrings.getString("http.method_post_not_supported");
	if (protocol.endsWith("1.1")) {
	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
	} else {
	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
	}
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
    {
	String protocol = req.getProtocol();
	String msg = lStrings.getString("http.method_get_not_supported");
	if (protocol.endsWith("1.1")) {
	    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
	} else {
	    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
	}
    }

这些说明在使用设计模式的时候并不是生搬硬套,而是根据具体的情况灵活变动。但是其本质是不变的。

4.  为什么会出现线程安全问题

在学习servlet过程中一直听到的一个问题就是线程问题,学习的资料中提到servlet是多线程,不安全的。

servlet是由web容器管理的,当请求这个servlet的时候,web容器会根据web.xml中的配置实例化这个servlet。当再次请求这个servlet的时候不会再实例化此servlet而是多线程的使用第一次请求的时候实例化的servlet。也就是一个servlet只实例化一次。

当面对多个请求的时候web容器就会使用多线程来处理这些请求。下面的图形是servlet多线程的结构图形

从图中可以看出,当容器收到一个请求servlet的请求的时候会从线程池中分出一个工作线程来处理这个请求,当有另外一个请求的时候会从线程池中分出另外一个工作线程来处理这个请求;所以如果多个用户同时请求同一个servlet,那么在容器中就会出现一个servlet对象的service方法并发执行。多线程是共享资源的,当他们并发执行的时候就很有可能出现访问资源冲突问题。

这种处理请求的方式就是我们经常说的单实例多线程的处理方式。

下面是自己查找到的一个作者:http://blog.csdn.net/zljjava/article/details/6887266从内存的角度来分析的servlet的多线程问题。如果有兴趣的话可以看看。

从内存中看一下这个问题可以总结下面的两种图形,其中一张是我们都知道JMM模型图

另外一张是多个用户请求同一个servlet的多线程执行的过程是:

主内存中的实例变量是在多个线程之间共享的,每个线程在自己的工作线程中拷贝自己需要的实例变量到自己的缓存中。就如同上面图形中的A,当线程1修改了A,但是线程2中的A还是主存中的样子,并没有马上同步到线程2和线程3中就会出现线程安全问题。

5. 总结

对于servlet的单实例多线程问题知道的原因就可以对症下药的找解决方案了。主要是针对变量要注意。对于模板方法可以看到我们的应用很多但是也要注意活学活用。

Servlet的模板方法和多线程

时间: 2024-08-30 04:14:05

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

sevlet是单线程还是多线程,在多线程下如何编写安全的servlet程序

sevlet是单线程还是多线程,在多线程下如何编写安全的servlet程序 首先明确:Servlet是单实例的,即对于同一种业务请求只有一个是实例.不同的业务请求可以通过分发来产生多个实例.其次:单实例的原因我想是因为单实例足可以处理某一个请求,就像ibatis的Querydao.UpdateDao一样都是单实例的.再次:为什么单实例足可以处理某一个请求,因为Servlet是单实例多线程的.http://hiyachen.cublog.cn  [email protected]先看一段代码:pa

Servlet单例多线程

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

servlet单实例多线程模式

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

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容器通过