默认情况下,第一次访问servlet的时候创建servlet对象。 如果servlet的构造方法或init方法中执行了比较多的逻辑代码,那么就会导致用户第一次访问sevrlet的时候比较慢。
解决方法:改变servlet创建对象的时机,即提前到加载web应用的时候!!!
在servlet的配置信息中,加上一个<load-on-startup>即可!!
<servlet> <servlet-name>LifeDemo</servlet-name> <servlet-class>com.rk.http.b_lifecycle.LifeDemo</servlet-class> <!-- 让servlet对象自动加载 --> <load-on-startup>1</load-on-startup><!-- 注意: 整数值越大,创建优先级越低!! --> </servlet> <servlet-mapping> <servlet-name>LifeDemo</servlet-name> <url-pattern>/life</url-pattern> </servlet-mapping>
程序开发人员自定义的Servlet类,一般继承自javax.servlet.HttpServlet抽象类,而javax.servlet.HttpServlet类继承自javax.servlet.GenericServlet抽象类。javax.servlet.GenericServlet提供了两个init方法:
void init(ServletConfig config)
void init()
两者之间的简单关系如下:
//有参数的init方法
//该方法是servlet的生命周期方法,一定会被tomcat服务器调用
//注意:如果要编写初始代码,不需要覆盖有参数的init方法
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
//无参数的init方法
//该方法是servlet的编写初始化代码的方法。
//是Sun公司设计出来专门给开发者进行覆盖,然后在里面编写servlet的初始逻辑代码的方法。
public void init() throws ServletException {
}javax.servlet.GenericServlet类的部分源码如下:
package javax.servlet;
import java.io.IOException;
import java.util.Enumeration;
public class GenericServlet implements Servlet, ServletConfig, java.io.Serializable
{
private transient ServletConfig config;
/**
* Does nothing. All of the servlet initialization is done by one of the <code>init</code> methods.
*/
public GenericServlet() { }
/**
* Called by the servlet container to indicate that the servlet is being placed into service.
*/
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
/**
* A convenience method which can be overridden so that there‘s no need
* to call <code>super.init(config)</code>.
*
* Instead of overriding init(ServletConfig), simply override
* this method and it will be called by <code>GenericServlet.init(ServletConfig config)</code>.
* The <code>ServletConfig</code> object can still be retrieved via getServletConfig.
*/
public void init() throws ServletException {
}
/**
* Returns this servlet‘s ServletConfig object.
*/
public ServletConfig getServletConfig() {
return config;
}
public String getServletName() {
return config.getServletName();
}
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
public Enumeration getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
/**
* Returns a reference to the ServletContext in which this servlet is running.
*/
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
}servlet对象在tomcat服务器是单实例多线程的。
因为servlet是多线程的,所以当多个servlet的线程同时访问了servlet的共享数据(如成员变量),可能会引发线程安全问题。
解决办法:
1)把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)
2)建议在servlet类中尽量不要使用成员变量。
a)如果确实要使用成员,必须同步;
b)尽量缩小同步代码块的范围,以避免因为同步而导致并发效率降低。(哪里使用到了成员变量,就同步哪里!!)
示例:记录页面访问次数
package com.rk.http.c_thread;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* servlet的多线程并发问题
* @author RK
*
*/
public class ThreadDemo extends HttpServlet
{
private int count = 0;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
// out.write("<html><head><title>访问次数</title></head><body>这是第N次访问本页面</body></html>");
out.write("<html><head><title>访问次数</title></head><body>这是第");
synchronized (ThreadDemo.class)//锁对象必须唯一。建议使用类对象
{
try
{
Thread.sleep(3000);//这里模拟复杂的业务等待
}
catch (InterruptedException e)
{
e.printStackTrace();
}
count++;
out.write("" + count);
}
out.write("次访问本页面</body></html>");
}
}原文:http://lsieun.blog.51cto.com/9210464/1782118