Web中三大属性范围的线程安全性分析及处理

     在Servlet中,我们可以将属性(内容)保存到三个基本的属性范围中,即Web应用上下文(Context)、会话(Session)以及请求(Request),以供Web应用中的其他组件在适当的时候获取。 但由于Servlet处于(大部分情况)多线程的环境中,所以对以上属性范围内容的操作也自然存在线程安全的问题。

 

1、先从可访问性、作用域及适用场景三个方面,介绍下三个属性作用域的区别。

 

    1)上下文(ServletContext)

        a)可访问性:Web应用的所有部分,包括Servlet、Jsp、监听器及过滤器等。

        b)作用域:ServletContext的生命期,即部署Web应用的生命期。

        c)适用场景:共享整个Web应用的资源,比如数据库连接、JNDI查找名及Email地址等。

 

    2)会话(HttpSession)

        a)可访问性:访问某个特定会话的所有Servlet或Jsp。

        b)作用域:会话的生命期,即会话未超时或未被撤销。

        c)适用场景:与客户会话有关的资源和数据,通常是针对同一客户的多个请求,它要与客户保持一个持续的会话状态。比如可以用来实现购物车功能等。

 

    3)请求(HttpServletRequest)

        a)可访问性:访问某个特定请求的Servlet、Jsp(通过请求转发)或监听器等。

        b)作用域:某个请求的生命期,即持续到Servlet的service()方法结束。

        c)适用场景:将模型返回的信息(或特定于某个请求的数据)从控制器传递(请求转发)给视图。

 

 2、下面探讨下线程安全性问题。

 

    1)上下文(ServletContex)作用域是线程安全的吗?如何解决?

 

    答:不是。因为上下文作用域共享于整个Web应用。每个请求都在一个单独的线程中处理,则当(同一个或多个用户的)多个请求(在相同或不同的Servlet中)同时操作上下文属性时,就有可能产生问题,以下举例说明。

 

    情况一:同一个Servlet操作相同的上下文属性。

 

    a)假设请求A到达ServletA,容器创建一个新的线程A来处理请求A。请求A在ServletA中设置了上下文属性“account”值为1000,并尝试在service()方法的最后获取并打印该属性“account“的值。我们期望的结果应该是打印出1000。但假如在获取并打印之前,发生了如下情况,结果就有可能出乎预料了。

    b)假设打印“account”之前,另一个新的请求B到达ServletA,容器又创建一个新的线程B来处理请求B。而且线程B的优先级比线程A高,所以容器会让线程B成为活动线程,而将线程A回到可运行的状态。不恰巧的是请求B也是来操作上下文属性“account”的,并把属性值改成了100。

    c)线程B执行完了,线程A也重新成为了活动线程,并继续打印“account”的值。此时将打印出100而不是1000。

    d)说明:如果在线程A打印之前耗费很长时间(如处理复杂的业务逻辑),则更容易出现线程安全问题。

 

     情况二:多个不同的Servlet操作相同的上下文属性。(例子参考情况一)

 

    解决方法:在Servlet的service()方法中同步上下文对象,即对上下文加锁。

synchronzized(getServletContext()){ 
    //设置属性     
   //调用业务逻辑     
   //获取属性值 
}

    

    注意:

        a)必须在处理上下文属性的所有代码中(比如其他Servlet中)对上下文对象进行同步。

        b)对上下文对象本身同步才有效,而直接同步service()方法是无效的(原因参考上述说明)。

 

    2)会话(HttpSession)作用域是线程安全的吗?如何解决?

 

    答:也不是。我们知道会话是用来维护服务器与某个客户的持续会话状态的。正常来说,一个客户一次只发送一个请求的话,那么任何给定时刻,在会话中一次也只有一个线程运行,这样看上去是线程安全的。不过,如果一个客户同时发送了多个请求(比如新建了多个浏览器窗口)呢?此时在一个会话中,就可能有多个线程运行,也就可能产生线程安全问题。

   

    解决方法:和上下文类似,在Servlet的service()方法中同步会话对象,即对会话对象加锁。

HttpSession session=request.getSession();
synchronzized(session){ 
    //设置属性     
   //调用业务逻辑     
   //获取属性值 
}

 

    注意(和处理上下文属性类似):

        a)必须在处理会话属性的所有代码中(比如其他Servlet中)对会话对象进行同步。

        b)对会话对象本身同步才有效,而直接同步service()方法是无效的。

 

     3)请求(HttpServletRequest)作用域是线程安全的吗?如何解决?

 

     答:是。我们知道请求作用域只与某个特定的请求相关,所以一个请求一次只会在一个单独的线程中运行,也就不存在线程安全性的问题了。

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值