Servlet线程安全的问题----总结(应付以后的面试)

首先,对于servlet来说,它的生命周期是:当服务器接收用户的一个请求,就会初始化一个servlet实例对象,之后调用init()方法来进行初始化。这个实例一旦创建,就会驻留在内存里面,以供后续的用户访问的需要。之后会调用service()方法,来进行分发调用,选择是调用doGet()还是doPost()方法。针对不同的用户,每人会有自己的一份request和response。最后,调用destroy()方法,来销毁实例对象。

那么servlet是如何同时处理多个请求的呢?

Servlet采用多线程来处理多个请求同时访问,Servelet容器维护了一个线程池来服务请求。线程池实际上是等待执行代码的一组线程叫做工作者线程,Servlet容器使用一个调度线程来管理工作者线程。当容器收到一个访问Servlet的请求,调度者线程从线程池中选出一个工作者线程,将请求传递给该线程,然后由该线程来执行Servlet的service方法。当这个线程正在执行的时候,容器收到另外一个请求,调度者线程将从池中选出另外一个工作者线程来服务新的请求,容器并不关心这个请求是否访问的是同一个Servlet还是另外一个Servlet。当容器同时收到对同一Servlet的多个请求,那这个Servlet的service方法将在多线程中并发的执行。servlet默认采用的是单实例多线程的方式来处理用户的请求的。这样减少产生servlet实例的开销,提升了服务器对请求的相应的时间。

线程安全的概念:当多个线程同一个时间访问同一个java实例的时候,不会出现异常。与我们在单线程下运行的结果是一样的。

那么对于servlet来说,线程的安全主要取决于我们多个线程共享的这个servlet实例。


import javax.servlet. *; 
import javax.servlet.http. *; 
import java.io. *; 
public class Concurrent Test extends HttpServlet {

    PrintWriter output; 
    public void service (HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        String username;
        Response.setContentType ("text/html; charset=gb2312");
        Username = request.getParameter ("username"); 
        output = response.getWriter (); 
        try{
            Thread. sleep (5000); //为了突出并发问题,在这设置一个延时
        } catch (Interrupted Exception e){}
        
        output.println("用户名:"+Username+""); 
    }
}

因为我们所有的线程是同用一个servlet实例,那么我们所定义的实例变量肯定是只有一份。当一个用户输入的username=a,此时output这个实例变量将会储存着输入到浏览器里面的数据。由于当前线程睡眠。与此同时,另外的用户输入username=b。这样就会覆盖掉我们先前在output里面存储的用户的数据。才会出现了线程不安全的问题。

由于实例变量的不正确的使用导致线程不安全,怎样来保证我们的servlet是线程安全的呢?

1、实现 SingleThreadModel 接口
  该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行(这种方法,可以,但是已经违背了线程安全的概念,线程安全强调的就是“多个线程同时访问”,但是如果一个Servlet实现了 SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销),当然也就不存在线程安全的问题。这种方法只要将前面的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){     //针对使用synchronized关键字,我们要尽量缩小锁的粒度。否则会使多用户出现阻塞状态,影响系统性能。
output = response.getWriter (); 
try {
    Thread. Sleep (5000);
} Catch (Interrupted Exception e){}
    output.println("用户名:"+Username+"
"); 

}
}

3、避免使用实例变量----最佳选择。
  本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。
  修正上面的Servlet代码,将实例变量改为局部变量实现同样的功能,代码如下:
…… 
public class Concurrent Test extends HttpServlet {
    public void service (HttpServletRequest request, 
            HttpServletResponse response) throws ServletException, IOException {
        PrintWriter output; 
        String username;
        response.setContentType ("text/html; charset=gb2312");
        …… 
    } 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值