Servlet的线程安全

Servlet与线程安全 Servlet与线程安全 先说结论,Servlet本身是单例的,线程安全的。但是如果引入共享变量,则可能会变得线程不安全。 1. 什么是线程安全 首先说明一下对线程安全的讨论,哪种情况我们可以称作线程安全? 《Java并发编程实战》给出的定义: 当多个线程访问某个类时,不管运行时环境采用何种调度方式,或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的... 阅读详情

源自:http://blog.youkuaiyun.com/yaerfeng/article/details/7385860

Servlet的多线程机制

 

1.  变量的线性安全:这里的变量指字段和共享数据(如表单参数值)。

  • 将参数变量本地化。多线程并不共享局部变量,所以要尽可能地在servlet中使用局部变量。例如:String user=request.getParameter("user");

  • 使用同步块Synchronized,防止可能异步调用的代码块,这就意味着线程需要排队处理。但要注意在使用同步块的范围要尽可能的小,不要直接在sevice方法和响应方法上使用,这样会严重影响性能。

2.  属性的线性安全:ServletContext,HttpSession,ServletRequest对象中的属性。

  • ServletContext(线程不安全):ServletContext可以同时进行多线程读/写属性,线程是不安全的。要对属性的读写进行同步处理或进行深度Clone()。所以在Servlet上下文中要尽量少保存会被修改(写)的数据,可以使用其他的方式在多个Servlet中共享,比如使用单例模式处理共享数据。

  • HttpSession(线程不安全):HttpSession在用户会话期间存在,只能在处理属于同一个Session请求的线程中被访问,因此理论上访问Session对象的属性是线程安全的。但是当用户打开同属于同一个进程的浏览窗口,对这些窗口的访问属于同一个session,会出现多次请求,需要多个工作线程来处理,可能会造成多个线程同时读写操作。这时我们就需要对属性的读写进行同步处理:使用同步块或读/写器来处理。

  • ServletRequest(线程安全):对于每一个请求,由一个线程来执行,都会创建一个新的ServletRequest对象,所以ServletRequest只能在一个线程中被访问。注意:ServletRequest对象在service方法的范围内是有效的,不要试图在service方法结束后仍然保存访问请求对象的引用。

3.  不要在Servlet中创建自己的线程以完成某个功能:servlet本身就是多线程,再创建线程会导致问题复杂化,会带来线程安全的问题。

 

4.  在多个Servlet中对外部对象(比如文件)修改一定要加锁,做到互斥的访问。

 

5.  javax.servlet.SingleThreadModel接口是一个标识接口,如果一个servlet实现了这个接口,那么servlet容器将保证在一个时刻仅有一个线程可以在给定的servlet实例的service方法中执行,将其他所有请求进行排队。

 

6.  服务器可以使用多个实例来处理请求,代替单个实例的请求排队带来的效益问题。服务器创建一个Servlet类的多个Servlet实例组成的实例池,对于每个请求分配Servlet实例进行响应处理,之后放回到实例池中等待下此请求。这样就造成并发访问的问题。此时,局部变量(字段)也是安全的,但对于全局变量和共享数据是不安全的,需要进行同步处理。而对于这样多实例的情况SingleThreadModel接口并不能解决并发访问问题。



  Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。
当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行,如图1所示。



图1 Servlet线程池


  这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。所以在用Servlet构建的Web应用时如果不注意线程安全的问题,会使所写的Servlet程序有难以发现的错误。

这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。所以在用Servlet构建的Web应用时如果不注意线程安全的问题,会使所写的Servlet程序有难以发现的错误。

  Servlet的线程安全问题

  Servlet的线程安全问题主要是由于实例变量使用不当而引起的,这里以一个现实的例子来说明。

[java] view plaincopy

  1. import javax.servlet. *;  

  2. import javax.servlet.http. *;  

  3. import java.io. *;  

  4. public class Concurrent Test extends HttpServlet {PrintWriter output;  

  5.     public void service (HttpServletRequest request,  

  6.             HttpServletResponse response) throws ServletException, IOException {  

  7.         String username;  

  8.         Response.setContentType ("text/html; charset=gb2312");  

  9.         Username = request.getParameter ("username");  

  10.         PrintWriter output = response.getWriter ();  

  11.         try{  

  12.             Thread. sleep (5000); //为了突出并发问题,在这设置一个延时  

  13.         } catch (Interrupted Exception e){}  

  14.          

  15.         output.println("用户名:"+Username+"");  

  16.     }  

  17. }  



  该Servlet中定义了一个实例变量output,在service方法将其赋值为用户的输出。当一个用户访问该Servlet时,程序会正常的运行,但当多个用户并发访问时,就可能会出现其它用户的信息显示在另外一些用户的浏览器上的问题。这是一个严重的问题。为了突出并发问题,便于测试、观察,我们在回显用户信息时执行了一个延时的操作。假设已在web.xml配置文件中注册了该Servlet,现有两个用户a和b同时访问该Servlet(可以启动两个IE浏览器,或者在两台机器上同时访问),即同时在浏览器中输入:

  a: http://localhost: 8080/servlet/ConcurrentTest? Username=a

  b: http://localhost: 8080/servlet/ConcurrentTest? Username=b

  如果用户b比用户a回车的时间稍慢一点,将得到如图2所示的输出:


图2 a用户和b用户的浏览器输出


  从图2中可以看到,Web服务器启动了两个线程分别处理来自用户a和用户b的请求,但是在用户a的浏览器上却得到一个空白的屏幕,用户a的信息显示在用户b的浏览器上。该Servlet存在线程不安全问题。下面我们就从分析该实例的内存模型入手,观察不同时刻实例变量output的值来分析使该 Servlet线程不安全的原因。

  Java的内存模型JMM(Java Memory Model)JMM主要是为了规定了线程和内存之间的一些关系。根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有实例变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存由缓存和堆栈两部分组成,缓存中保存的是主存中变量的拷贝,缓存可能并不总和主存同步,也就是缓存中变量的修改可能没有立刻写到主存中;堆栈中保存的是线程的局部变量(save in stack, not in heap),线程之间无法相互直接访问堆栈中的变量。根据JMM,我们可以将论文中所讨论的Servlet实例的内存模型抽象为图3所示的模型。


图3 Servlet实例的JMM模型


  下面根据图3所示的内存模型,来分析当用户a和b的线程(简称为a线程、b线程)并发执行时,Servlet实例中所涉及变量的变化情况及线程的执行情况,如图4所示。

调度时刻a线程b线程
T1访问Servlet页面
T2
访问Servlet页面
T3output=a的输出username=a休眠5000毫秒,让出CPU
T4
output=b的输出(写回主存)username=b休眠5000毫秒,让出CPU
T5在用户b的浏览器上输出a线程的username的值,a线程终止。
T6
在用户b的浏览器上输出b线程的username的值,b线程终止。

                  图4 Servlet实例的线程调度情况

  从图4中可以清楚的看到,由于b线程对实例变量output的修改覆盖了a线程对实例变量output的修改,从而导致了用户a的信息显示在了用户b的浏览器上。如果在a线程执行输出语句时,b线程对output的修改还没有刷新到主存,那么将不会出现图2所示的输出结果,因此这只是一种偶然现象,但这更增加了程序潜在的危险性。


设计线程安全的Servlet

  通过上面的分析,我们知道了实例变量不正确的使用是造成Servlet线程不安全的主要原因。下面针对该问题给出了三种解决方案并对方案的选取给出了一些参考性的建议。

  1、实现 SingleThreadModel 接口

  该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。这种方法只要将前面的Concurrent Test类的类头定义更改为:

public class Concurrent Test extends HttpServlet implements SingleThreadModel {
…………
}

  2、同步对共享数据的操作

  使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,在本论文中的Servlet可以通过同步块操作来保证线程的安全。同步后的代码如下:

…………

[java] view plaincopy

  1. Public class Concurrent Test extends HttpServlet {  

  2.     …………  

  3. username = request.getParameter ("username");  

  4. synchronized (this){  

  5. output = response.getWriter ();  

  6. try {  

  7.     Thread. Sleep (5000);  

  8. } Catch (Interrupted Exception e){}  

  9.     output.println("用户名:"+Username+"  

  10. ");  

  11. }  

  12. }  

  13. }  



  3、避免使用实例变量

  本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。

  修正上面的Servlet代码,将实例变量改为局部变量实现同样的功能,代码如下:

……

[java] view plaincopy

  1. public class Concurrent Test extends HttpServlet {  

  2.     public void service (HttpServletRequest request,  

  3.             HttpServletResponse response) throws ServletException, IOException {  

  4.         PrintWriter output;  

  5.         String username;  

  6.         response.setContentType ("text/html; charset=gb2312");  

  7.         ……  

  8.     }  

  9. }  



  对上面的三种方法进行测试,可以表明用它们都能设计出线程安全的Servlet程序。但是,如果一个Servlet实现了 SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销。 SingleThreadModel在Servlet2.4中已不再提倡使用;同样如果在程序中使用同步来保护要使用的共享的数据,也会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化 Servlet 中的同步代码;在Serlet中避免使用实例变量是保证Servlet线程安全的最佳选择。从Java 内存模型也可以知道,方法中的临时变量是在栈上分配空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全。

  小结

  Servlet的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写Servlet程序时要特别注意。线程安全问题主要是由实例变量造成的,因此在Servlet中应避免使用实例变量。如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。


Servlet的线程安全问题 文章目录Servlet的线程安全问题引入Servlet的多线程机制如何修复1.实现 SingleThreadModel 接口2.避免使用成员变量3.同步对共享数据的操作思考与小结参考文章 Servlet的线程安全问题 引入 首先看看这样的代码,有什么问题 这里既要求cmd不能包含Calculator又必须要包含Calculator,能做到吗,当然是可以的 Servlet的多线程机制 Servlet实际上是一个单件,当我们第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件或者是 阅读详情

相关推荐

基于文件通信的QMT量化交易信号桥接方案设计与实现

在量化交易系统开发中,进程间通信(IPC)是连接不同计算模块、实现策略逻辑与交易执行解耦的关键技术。其核心原理在于允许运行于独立环境或进程的程序安全、可靠地交换数据。文件系统通信作为一种经典的IPC方法,因其实现简单、跨平台兼容性好且数据可持久化,在非高频场景下具有重要技术价值。它尤其适用于需要将外部复杂计算环境(如配备丰富数据科学库的Python环境)与专用交易终端(如迅投QMT)集成的应用场景。通过约定文件路径与数据格式,外部程序可将包含交易指令的JSON信号文件写入特定目录,而交易终端通过轮询或监听机

weixin_28678151的博客 415

Servlet线程安全问题

Servlet线程安全问题,及解决方案

weixin_53798697的博客 524

仪表数字图像识别的传统方法

虽然现有很多机器学习算法能够准确地识别数字,但是传统方法对于一些需要离线识别且计算能力有限的下位机设备来说,仍然有非常不错的效果。因此,今天就向大家讲解一下传统的数字识别方法在仪表读数方面的应用。 1.图像预处理技术 1.1 主要流程 图像处理依次需要进行图片预处理、字轮区域定位、字符分割和字符识别等过程。其中图像处理流程如下图所示。 2. 详细过程 2.1 灰度化 为了减小图像原始数据量,本系...

XYZ 4521

Servlet线程安全性

Servlet 是线程安全的主要原因是因为 Servlet 容器负责在多个线程中调用 Servlet 实例的方法。当 Servlet 容器收到一个请求时,它会创建一个新的线程并调用该请求所需的 Servlet 实例的 service() 方法。因为每个请求都会创建一个新的线程并调用 Servlet 实例的方法,所以多个请求不会同时访问同一个 Servlet 实例的状态,因此 Servlet 实例的状态不会受到其他线程的影响。此外,Servlet API 还提供了一些支持线程安全的机制。

m0_74327508的博客 212

MCAL配置之CAN模块及源码分析

该文章详细解析了Autosar中MCAL配置里的CAN模块,详细分析了重要的配置选项,并对CAN的发送和接收函数进行了剖析。

u013624141的博客 1416

SAP CO实战:如何用KKA2/CJ88结果分析码搞定项目收入成本匹配(附真实案例)

本文深入探讨了SAP CO模块中KKA2和CJ88结果分析码在项目收入成本匹配中的高阶应用。通过真实案例解析,详细介绍了这两种分析码在分阶段收入确认和终验法项目中的配置技巧与实战经验,帮助财务人员高效解决复杂业务场景下的财务数据处理难题。

weixin_29230773的博客 166

Javaweb学习笔记之HttpServlet(四):Servlet 的多线程并发问题

Servlet 的多线程并发问题: package com.demo.f_thread; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; i...

秋忆夏伤的博客 2049

从寄存器到驱动:深入解读RFSoC RF-ADC阈值与校准API的底层逻辑

本文深入解析Xilinx RFSoC中RF-ADC的阈值检测与校准API的底层逻辑,涵盖寄存器映射、驱动实现及Gen3增强功能。通过详细代码示例和寄存器操作流程,揭示如何精准控制信号阈值检测与校准时序,提升系统稳定性与性能。特别探讨RF数据转换器在无线通信和雷达信号处理中的关键应用。

weixin_30552811的博客 687

servlet -线程安全

线程安全是 Java 多线程编程中的一个重要概念,特别是在 Web 应用开发中,正确处理线程安全可以避免数据不一致、竞态条件和死锁等问题。Servlet 作为 Java Web 开发的核心组件,由于其单实例多线程的特性,必须特别注意线程安全性。通过合理使用同步方法、同步块、原子类以及无状态设计等方法,可以有效提高 Servlet 的线程安全性和性能。在设计和开发过程中,开发者需要根据具体需求选择合适的同步机制,确保系统在高并发环境下的稳定性和可靠性。

Flying_Fish_roe的博客 1016

【知识分享】IBIS 建模指南 03

本文介绍了差分缓冲器的数据提取方法,重点分析了差分系统与单端系统的核心差异。文章详细阐述了差分接收机和驱动器的结构特点、工作原理及建模方法,指出全差分结构具有优异的电源噪声抑制能力。针对IBIS建模规范,作者提出了三维I-V曲面数据提取技术,并介绍了共模/差模电流分离、片上端接处理以及V-T表生成的解决方案。文章还探讨了差分电容(C_diff)的测量方法和数据简化策略,为复杂差分缓冲器的精确建模提供了系统化的技术路径。

一只豌豆象的博客 1281

LVGL colorwheel组件教程一(调色板)

【代码】LVGL colorwheel组件教程一(调色板)

摸鱼专家 427

什么是线程安全?Servlet 是线程安全的吗?

线程安全是指在多线程环境下访问共享资源时,系统的行为能够保持一致性和正确性。换句话说,当多个线程同时访问或修改同一个对象时,线程安全保证这些线程的执行不会导致数据的不一致性或产生意料之外的结果。在 Java 中,线程安全同步(Synchronization):通过加锁的方式确保同一时刻只有一个线程可以访问共享资源。不可变性(Immutability):通过将对象设计为不可变(即一旦创建,内部状态不能被修改),来保证线程安全。局部变量。

lssffy的博客 1034

浅谈银行内部户治理

为避免该问题的发生,对内部户入账类交易,或内部户是可透支的账户(如系统间往来),可考虑通过异步方式更新账户余额(当然,根本上还是要考虑银行业务量的实际,异步消息接口很多时候没有同步接口方便),另外,在进行内部户余额更新时,可以通过定时的借贷方发生额轧差的方式,将多次的锁请求,简化为一次(当然,发生额明细本身在加工账户交易历史时,是不能汇总的)。内部户在银行系统中,属于业务属性较少,实现相对简单,原理也很容易讲清的系统,但管中窥豹,越是基础的东西,越能看出一家银行业务、内控的专业程度。

银行信息系统应用架构详解 786

Servlet线程安全

概述 在探讨java线程安全前,让我们先简要介绍一下Java语言。 任何语言,如C++,C#,Java,它们都有相通之处,特别是语法,但如果有人问你,Java语言的核心是什么?类库?关键字?语法?似乎都不 是。Java语言的核心,也就是Sun始终不愿意开源的东西:Java虚拟机的实现(不...

chouyin9341的博客 151

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

servlet默认是线程安全的吗

Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。 当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行,如图1所示。 这样,

生命在于折腾 3941

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

servlet线程安全

首先我们必须要先了解servlet的生命周期:服务器只创建每个servlet的单一实例,首次创建servlet时,它的init方法会被调用,因此,init是放置一次性设置代码的地方,之后,针对每个用户的请求都会创建一个线程,该线程调用前面创建的实例方法。多个并发请求一般会导致多个线程同时调用service(线程安全),service方法会依据接受到HTTP请求的类型,调用doXXX方法。最后如

longtramp的专栏 4080

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

servlet线程安全吗?

目录servlet线程安全吗?servlet执行流程web 容器能为每个请求创建一个Servlet的实例吗?SingleThreadModelspringmvc是线程安全的吗?springmvc singleton有几种解决方法总结 servlet线程安全吗? servlet默认是单例模式,在web容器中只创建一个实例,所以多个线程同时访问的时候,是非线程安全的。 servlet执行流程 客户端发送请求给服务器。 服务器开始接受,先判断该请求的servlet实例是否存在,如果不存在先装载一个servle

weixin_49374214的博客 969

servlet开发之servlet的线程安全问题

之所以考虑线程安全问题,是因为引入了多线程。多线程指的是这个程序(一个进程)运行时产生了不止一个线程。如果不考虑多线程的话,程序执行只有一条路径,就像人在敲代码的时候只能敲代码,不能戴上耳机听歌。引入多线程,人在敲代码的时候还能听歌,解决一件事情的时候做了另一件事情,效率大大提高了(虽然敲代码的时候听歌不是为了听歌)。用多线程只有一个目的,那就是更好的利用cpu的资源。 Servlet/JSP技...

weixin_40273144的博客 390

Servlet线程安全的解决方法

摘自:http://developer.51cto.com/art/200907/133827.htm   Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。 当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Ser

伍佰的专栏 1074

Servlet是线程安全的吗?

前两天,有个人问我“servlet是线程安全的吗?“,我却很难给出一个很具体清晰的回答,今天重新整理一下思路,也复习一下那些被扔回给老师的理论。 servlet是线程安全的吗? 这个问题,在网上没有看到一个确切的答案,所以我们来分析一下: 首先什么是线程安全?  引用概念:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的

jiangxindu1的专栏 535

Servlet 是线程安全的吗?

1、Servlet 默认是使用单例模式创建的 2、多个线程同时请求同一个 servlet 的时候,将会并发的调用 service() 方法,如果 servlet 中存在实例变量,那么将可能会引发线程安全问题 3、综上,servlet 不是线程安全的 参考资料: 1、Java面试题:Servlet是线程安全的吗? https://www.cnblogs.com/chanshuyi/p/...

HeatDeath的博客 1993

描述Servlet生命周期,Servlet是线程安全的吗,为什么?

当Servlet容器启动后,它必须要知道所需的Servlet类在什么位置,Servlet容器可以从本地文件系统、远程文件系统或者其他的网络服务中通过类加载器加载Servlet类,成功加载后,容器创建Servlet的实例。因为容器是通过Java的反射API来创建Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法),所以我们在编写Servlet类的时候,不应该提供带参数的构造方法。b.重写init()方法,doGet()方法,doPost()方法,destroy()方法。

qq_59510147的博客 597
上一篇: 《Servlet学习笔记》Servlet开发细节-线程安全
下一篇: Insufficient space for shared memory file
架构师的清醒
博客等级 码龄14年 291粉丝 397原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值