Java Web -- Servlet(7) Servlet的线程安全问题、ServletConfig对象


Servlet的线程安全问题

 

Servlet是一个供Servlet引擎(Web服务器)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。针对客户端的多次Servlet请求,通常情况下,服务器只创建一个Servlet实例对象也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其他请求服务器,直至Web容器退出(关闭浏览器),Servlet实例对象才会销毁。

注意:

一个Servlet被客户端发送的第一请求激活,然后将继续运行于后台,等待以后的请求。每个请求将生成一个新的线程,而不是一个完整的进程。

Servlet的整个生命周期内,Servletinit方法和destroy方法都只被调用一次。而当客户端的每次访问该ServletServlet引擎就会调用一次service()方法。所以每次requestresponse都是全新的封装对象。(即每个用户调用Servlet时,requestresponse都不一样的)。

Servlet第一次被访问后,就被加载到内存中,以后该实例对各个请求服务,所以就引发了单例问题。

 

举例:Servlet成员变量和局部变量的例子。

创建一个Web Project demo(Web应用)

demo下创建一个Servletdemo1.java),代码如下:

package com.focus.Thread;

 

import java.io.IOException;

import java.io.PrintWriter;

 

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

public class demo1 extends HttpServlet {

//定义一个整型的成员变量。

int i = 0;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html;charset=utf-8");

PrintWriter out = response.getWriter();

//定义一个局部变量。

int j = 0;

i++;

j++;

out.println("成员变量的值 i = "+i+"局部变量的值 j = "+j);

}

}

部署:<servlet-mapping>

        <servlet-name>demo1</servlet-name>

        <url-pattern>/demo</url-pattern>

    </servlet-mapping>

    <servlet>

        <servlet-name>demo1</servlet-name>

        <servlet-class>com.focus.Thread.demo1</servlet-class>

</servlet>

效果如下:

 

Java Web -- Servlet(7) Servlet的线程安全问题、ServletConfig对象_第1张图片 

Java Web -- Servlet(7) Servlet的线程安全问题、ServletConfig对象_第2张图片 

由此可以看出,成员变量是共享的数据,因为Servlet是以多线程模式执行的,当有多个客户同时并发请求一个Servlet时,容器将启动多个线程调用相应的请求处理方法,此时,请求处理方法中的局部变量是安全的,但对于成员变量和共享数据是不安全的,因为这多个线程有可能同时都操作到这些数据,此时就需要同步处理。

 

线程安全问题。

举例:售票系统

demo1.java代码如下:

package com.focus.Thread;

 

import java.io.IOException;

import java.io.PrintWriter;

 

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

public class demo1 extends HttpServlet {

int ticket = 2;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html;charset=utf-8");

PrintWriter out = response.getWriter();

if(ticket>0){

System.out.println("恭喜您,您买到票了。");

//此处的意图是要模拟当访问数量很多的时候。休眠10秒。

try{

Thread.sleep(1000*10);

}catch(InterruptedException e){

e.printStackTrace();

}

ticket--;

}else{

out.println("不好意思,我们的票已售完,请下次早点。");

}

}

}

以下是当浏览器不断刷新时,服务器窗口所显示。

 

当过了10秒后。

Java Web -- Servlet(7) Servlet的线程安全问题、ServletConfig对象_第3张图片 

处理方法:就是把代码加上同步机制。

public class demo1 extends HttpServlet {

int ticket = 2;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html;charset=utf-8");

PrintWriter out = response.getWriter();

synchronized(this){

if(ticket>0){

System.out.println("恭喜您,您买到票了。");

try{

Thread.sleep(1000*10);

}catch(InterruptedException e){

e.printStackTrace();

}

ticket--;

}else{

out.println("不好意思,我们的票已售完,请下次早点。");

}

}

}

总结:

(1)使用synchronized可同步操作同步变量和共享数据的代码,就可以防止可能出现的线程安全问题,但这也意味着线程需要排队处理。因此,要缩短使用的范围。

Synchronized(对象){

//同步代码

}

2尽量少使用成员变量和共享数据。如果一个变量不需要共享,则把该变量设为局部变量,就是直接在doGet()或者doPost()方法中定义,这样也就不会存在单例安全问题了。

 

 

 

ServletConfig接口

在初始化过程中,Servlet容器使用ServletConfig接口的对象作为参数来传递Servlet的配置信息。

方法如下:

getServletName(): 用于获取Servlet实例的名称。

getInitParameter(): 检索初始化参数的值,如果参数不存在,则返回null

getServletContext(): 返回Servlet用来与其容器交互的ServletContext对象。

 

ServletConfig对象用于读取servlet的配置信息

举例说明:

创建一个Servlet(demo2.java)

web.xml中添加标签如下:

  <servlet>

    <servlet-name>demo2</servlet-name>

<servlet-class>com.focus.Thread.demo2</servlet-class>

<!--下面是给Servlet配置信息,给配置的信息,只能有该Servlet读取-->

    <init-param>

    <param-name>encoding</param-name>

    <param-value>utf-8</param-value>

    </init-param>

  </servlet>

demo2.java代码如下:

package com.focus.Thread;

 

import java.io.IOException;

import java.io.PrintWriter;

 

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

public class demo2 extends HttpServlet {

 

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html");

//通过这种方式获得配置信息。

String encoding = this.getServletConfig().getInitParameter("encoding");

response.setCharacterEncoding(encoding);

PrintWriter out = response.getWriter();

out.println("编码方式为:"+encoding);

}

}

效果如下:

Java Web -- Servlet(7) Servlet的线程安全问题、ServletConfig对象_第4张图片 

而上面的这种配置只能由创建的Servlet读取,当想要全部Servlet都可以使用的配置信息时,可采用以下的方法:

<!--下面配置的参数,可被所有Servlet读取-->
<context-param>

<param-name></param-name>

<param-value></param-value>

</context-param>

 

当配置的信息有很多,想要把配置信息都获取到,代码如下:

配置信息如下:

    <init-param>

    <param-name>encoding</param-name>

    <param-value>utf-8</param-value>

    </init-param>

     <init-param>

    <param-name>name</param-name>

    <param-value>focus</param-value>

    </init-param>

     <init-param>

    <param-name>1</param-name>

    <param-value>2</param-value>

</init-param>

demo2.java添加如下代码:

//获取配置的所有名字

Enumeration<String> names = this.getServletConfig().getInitParameterNames();

//循环判断还有没有下一个元素

while(names.hasMoreElements()){

String name = names.nextElement();

String value = this.getServletConfig().getInitParameter(name);

out.println("<br/>配置的名字:"+name+"配置的值:"+value);

}

效果如下:

Java Web -- Servlet(7) Servlet的线程安全问题、ServletConfig对象_第5张图片 

你可能感兴趣的:(java,Web,servlet,servletconfig,servlet单例)