Servlet的生命周期、线程安全性问题、到底是单例还是多例?

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

什么是Servlet

Servlet(Server Applet) 是基于 Java 技术的 web 组件,该组件由容器托管,用于生成动态内容。他是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。

什么是 Servlet 容器

Servlet容器是web server或application server的一部分,供基于请求/响应发送模型的网络服务,解码基
于 MIME 的请求,并且格式化基于 MIME 的响应。Servlet 容器也包含并管理 Servlet 生命周期。

所有 Servlet 容器必须支持基于 HTTP 协议的请求/响应模型,比如像基于 HTTPS(HTTP over SSL)协议的 请求/应答模型可以选择性的支持。容器必须实现的 HTTP 协议版本包含 HTTP/1.0 和 HTTP/1.1。

Servlet 生命周期

Servlet 通过一个定义良好的生命周期来进行管理,该生命周期规定了 Servlet 如何被加载、实例化、初始化、 处理客户端请求,以及何时结束服务。该生命周期可以通过 javax.servlet.Servlet 接口中的 init、service 和 destroy 这些 API 来表示,所有 Servlet 必须直接或间接的实现 GenericServlet 或 HttpServlet 抽象类。

Servlet的生命周期有四个阶段:加载并实例化、初始化、请求处理、销毁。主要涉及到的方法有init、service、doGet、doPost、destory等

加载并实例化

Servlet容器负责加载和实例化Servelt。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。当Servlet容器启动后,Servlet通过类加载器来加载Servlet类,加载完成后再new一个Servlet对象来完成实例化。

初始化

在Servlet实例化之后,容器将调用init()方法,并传递实现ServletConfig接口的对象。在init()方法中,Servlet可以部署描述符中读取配置参数,或者执行任何其他一次性活动。在Servlet的整个生命周期类,init()方法只被调用一次。

请求处理

当Servlet初始化后,容器就可以准备处理客户机请求了。当容器收到对这一Servlet的请求,就调用Servlet的service()方法,并把请求和响应对象作为参数传递。当并行的请求到来时,多个service()方法能够同时运行在独立的线程中。service()方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet()、doPost()、doPut(),doDelete() 等方法。

一般情况下,当开发基于 HTTP 协议的 Servlet 时,Servlet 开发人员只关注自己的 doGet 和 doPost 请求处理 方法即可。其他方法被认为是非常熟悉 HTTP 编程的程序员使用的方法。

销毁

一旦Servlet容器检测到一个Servlet要被卸载,这可能是因为要回收资源或者因为它正在被关闭,容器会在所有Servlet的service()线程之后,调用Servlet的destroy()方法。然后,Servlet就可以进行无用存储单元收集清理。这样Servlet对象就被销毁了。这四个阶段共同决定了Servlet的生命周期。

Servlet的线程安全问题


为了有效利用JVM允许多个线程访问同一个实例的特性,来提高服务器性能。在非分布式系统中,Servlet容器只会维护一个Servlet的实例。

如果 Web 应用中的 Servlet 被标注为分布式的,容器应该为每一个分布式应用程序的 JVM 维护一个 Servlet 实例池。

Servlet容器通过维护一个线程池来处理多个请求,线程池中维护的是一组工作者线程(Worker Thread)。Servlet容器通过一个调度线程(Dispatcher Thread)来调度线程池中的线程。

当客户端的servlet请求到来时,调度线程会从线程池中选出一个工作者线程并将请求传递给该线程,该线程就会执行对应servlet实例的service方法。同样,当客户端发起另一个servlet请求时,调度线程会从线程池中选出另一个线程去执行servlet实例的service方法。Servlet容器并不关心这些线程访问的是同一个servlet还是不同的servlet,当多个线程访问同一个servlet时,该servlet实例的service方法将在多个线性中并发执行。

所以,Servlet对象是单实例多线程,Servlet不是线程安全的

为什么不安全?

先看两个定义:
实例变量:实例变量在类中定义。类的每一个实例都拥有自己的实例变量,如果多个线程同时访问该实例的方法,而该方法又使用到实例变量,那么这些线程同时访问的是同一个实例变量,会共享该实例变量。

局部变量:局部变量在方法中定义。每当一个线程访问局部变量所在的方法时,在线程的堆栈中就会创建这个局部变量,线程执行完这个方法时,该局部变量就被销毁。所有多个线程同时访问该方法时,每个线程都有自己的局部变量,不会共享。

看如下代码:

public class MyServlet extends HttpServlet{
 private static final long serialVersionUID = 1L;

 private String userName1 = null;//实例变量

 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException{
  userName1 = req.getParameter("userName1");

  String userName2 = req.getParameter("userName2");//局部变量

  //TODO 其他处理
 }
}

userName1则是共享变量,多个线程会同时访问该变量,是线程不安全的。

userName2是局部变量,不管多少个线程同时访问,都是线程安全的。

解决Servlet的线程安全问题

如果不涉及到全局共享变量,就直接使用局部变量

如果使用到全局共享的场景,可以使用加锁的方式.对全局变量的读写操作置于synchronized同步块中,这样不同线程排队依次执行该代码块,从而避免线程不安全情况发生。还可以使用线程安全的数据类型。比如hashtable,blockQueue等

Servlet是单例还是多例?


在Servlet规范中,对于Servlet单例与多例定义如下:

“Deployment Descriptor”, controls how the servlet container provides instances of the servlet.For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration. However, for a servlet implementing the SingleThreadModel interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.

上面规范提到,
如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。

而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。实例有多少呢,这里没细说。

下面再从Tomcat的源码中找寻下具体的参考实现是什么样子的。以下代码来源于Tomcat的StandardWrapper类。我把其中不太相关的部分做了删除。

public Servlet allocate() throws ServletException {
boolean newInstance = false;
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null) {
synchronized (this) {
if (instance == null) {
try {
instance = loadServlet();
} catch (ServletException e) {}}}}
if (singleThreadModel) {
if (newInstance) {
synchronized (instancePool) {
instancePool.push(instance); //如果实现STM接口,就放到一个栈里
nInstances++;
}}
} else {
if (!newInstance) {
countAllocated.incrementAndGet();
}
return (instance);
}
}
synchronized (instancePool) {
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}} }
countAllocated.incrementAndGet();
return instancePool.pop();
}}
/**
* Load and initialize an instance of this servlet, if there is not already
* at least one initialized instance. This can be used, for example, to
* load servlets that are marked in the deployment descriptor to be loaded
* at server startup time.
*/
public synchronized Servlet loadServlet() throws ServletException {
// Nothing to do if we already have an instance or an instance pool
if (!singleThreadModel && (instance != null))
return instance; //注意此处,如果存在实例就直接返回
Servlet servlet;
try {
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
}
if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<>();
} //此处,使用Stack存放STM的Servlet
singleThreadModel = true;
}
initServlet(servlet);
} finally {
}
return servlet;
}

那一个实现了SingleThreadModel接口的Servlet,一般会初始化多少个实例呢?
StandardWrapper类中有两个属性,其中maxInstance初始为20。所以上面的问题就有了答案。

/**
 * Does this servlet implement the SingleThreadModel interface?
 */
protected volatile boolean singleThreadModel = false;
/**
 * Maximum number of STM instances.
 */
protected int maxInstances = 20;

由于SingleThreadModel已经声明为废弃,官方不建议使用。我们这里只是让大家了解下。

总结下,一个Servlet究竟有几个实例呢?受如下几个原因影响:
是否在分布式环境中部署
是否实现SingleThreadModel,如果实现则最多会创建20个实例
在web.xml中声明了几次,即使同一个Servlet,如果声明多次,也会生成多个实例。

 

转自 

https://www.hollischuang.com/archives/849

https://www.jianshu.com/p/caba33d8f3fd

深入剖析STM32F103 I2C总线死锁问题及高效应对策略 本文深入剖析了STM32F103硬件I2C总线在轮询模式下发生死锁的根本原因,即硬件状态机的预取机制与软件响应速度不匹配。文章提供了高效的解决方案,包括在轮询模式下提前发送NACK/STOP信号、降低总线速度,以及治本的中断与DMA应用策略,帮助开发者根据项目需求选择最合适的方案,彻底解决这一经典硬件缺陷问题。 阅读详情

相关推荐

从DeepMimic到虚拟偶像:用动作捕捉数据训练AI跳舞的完整流程(附Blender插件配置)

本文详细介绍了如何利用DeepMimic的深度强化学习框架,将动作捕捉数据转化为虚拟偶像或游戏角色的舞蹈动作。从核心的模仿学习理念、PyBullet环境搭建、策略网络与奖励函数设计,到数据处理、训练技巧(如参考状态初始化),最后讲解了如何将训练好的AI模型集成到Blender和Unity等工具中,实现从数据到实时驱动角色的完整流程。

weixin_29054399的博客 502

Servlet的多线程单实例导致的数据同步问题

Servlet类本质上也是一个普通的类,并且Servlet容器默认只允许单个实例存在。当请求达到服务器的时候,Servlet实例如果已经存在的话则直接加载该实例,如果该Servlet类还未实例化则会先初始化这个Servlet。当请求到达Web服务器时,Web服务器中有一个线程池,它会从线程池中取一个工作线程,通过该线程调用请求的Servlet。因此,对Servlet来说,可以同时被好几个请求调用。...

weixin_33946605的博客 250

FreeRTOS --(15)信号量之概述

FreeRTOS 中使用信号量来做同步,信号量可以在任务中使用作为任务与任务间的同步,也可以在中断中使用(带 FromISR 的版本)中断与任务间的同步; 针对不同的应用场景,信号量分为两种: 1、二值信号量; 2、计数信号量; 1、二值信号量 1.1、Usage 顾名思义,二值信号量只有两个值:0 和 1;它用于简单场景下的任务与任务、中断与任务之间的同步,比如:一个任务,在等待某个资源到位后,才能够继续执行,在得到这个资源之前,它处于阻塞状态,假如一个中断来了,给出了这个资源,那么.

StephenZhou 6517

Servlet单例多例问题

Servlet到底是单例还是多例我们说了都不算,要看官方说明。 结论: Servlet既可以是单例,又可以是多例。Servlet 如果是同一个 Web App 的话,定义多次就是多例。所以要看容器,不能一概而论。 先看Servlet 2.3规范: 可以看出当Servlet实现类实现了SingleThreadModel接⼝后,他就是属于线程安全的,即多例的(对于一个服务让Single...

【欢迎关注公众号:冬瓜白】 5176

DQN网络代码调用env.reset()后存储观测信息报错问题ValueError: setting an array element with a sequence.解决处理

**reset()函数返回的是一个array类型以及其中数据的type!模拟游戏进行学习任务。因此需要将observation指定为真正需要的array信息即可,得到的的变量observation类型与Agent存储时的。因此怀疑返回的observation有问题,于是通过。报错信息提示为数据维度不对应,也即最初通过。在强化学习DQN网络代码实现过程中,针对。

Vulcan_Q的博客 4092

LabVIEW电压信号采集系统:从零搭建到科研实战

本文详细介绍了如何从零开始搭建一个基于LabVIEW的电压信号采集系统。内容涵盖硬件选型(如NI USB-6002数据采集卡)、软件环境配置、核心程序开发(包括多通道同步采集、数据自动保存)以及高级功能优化。系统支持自定义采样率与时长,并提供了完整的LabVIEW程序框图和配套的Python数据分析脚本,旨在帮助科研人员和工程师快速构建稳定、高效的专属数据采集方案,提升实验自动化水平。

gold8的专栏 460

Servlet是单例还是多例

Servlet本身只是一个Java,并不是单例结构,所以Servlet是不是单例最终取决于运行它的Web容器。 注解加载时默认是单例的。 如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。 如果在web.xml中声明了几次,即使同一个Servlet,如果声明多次,也会生成多个实例。 单实例多线程 Servlet容器默认是采用单实例多线程的方式处理多个请求的: 当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例

哈 3707

基于STM32的半导体制冷片温度控制

制冷片温度控制,温控范围15℃~45℃,本人菜鸡一枚,大佬绕路。 #include "stm32f10x_gpio.h" #include "relay.h" void relay_Init(void){ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//Ê...

大白兔的博客 9014

Springboot @Autowired依赖注入HttpServletRequest 和 HttpServletResponse 原理(居然不是单例)

通常来说依赖注入的对象一旦创建完成后就不会在改变,因为Spring的默认行为创建的都是单例对象。和不是单例对象,它们都是根据HTTP请求进行创建的,一个HTTP请求对象一个request和response。但是通过@Autowired可以实现每当一个请求进来时使用的都是当前的request或response对象,演示代码如下。

HD243608836的博客 6836

手把手教你学simulink(69.3)--毫米波5G/6G 系统场景实例设计并实现基于Simulink的毫米波多用户MIMO通信系统模型

毫米波频段(30 GHz到300 GHz)因其高带宽和高速率特性,成为5G和未来6G通信系统的理想选择。然而,毫米波信号的传播特性较差,容易受到大气吸收、雨衰、建筑物遮挡等因素的影响。为了克服这些挑战并提高通信容量,多用户MIMO(MU-MIMO, Multi-User Multiple-Input Multiple-Output)技术被广泛应用。多用户MIMO技术通过在基站和多个用户之间同时传输多个数据流,充分利用空间资源,从而显著提升系统容量和频谱效率。

MHD0815的博客 493

一个超酷的开源uHand2.0机械手掌项目

uHand2.0是深圳乐幻索尔公司开源的一款机械手掌,它长下面这个样子:1、uHand2.0外观图之前在公众号就分享过视频:学习嵌入式可以带娃,不信你们看2、uHand2.0硬件原理图看...

Bruce.yang的嵌入式之旅 4460

Servlet的单例多线程问题

一直想不通Java Servlet既然在服务器中是单例存在,而他的service方法又不是sychronized,而且他是多线程的,那么我同时有两个线程访问Servlet中的service方法,那么会不会导致service方法执行混乱,即第一个线程刚进入servlet了,第二个线程已经返回结果了,那么第一个线程是不是还没运行就结束了? 就是我把servlet中的service方法的运行当成了一个...

AlbenXie的博客 741

15、分布式查询处理详解

本博文深入探讨了分布式环境下的查询处理与优化技术。从查询处理的基本概念出发,分析了在分布式系统中执行查询所面临的额外挑战,并通过实例比较了不同的执行策略。文章详细介绍了查询优化的组成要素,包括搜索空间、成本模型和搜索策略,同时涵盖了查询处理的多个层次以及数据本地化的核心方法。此外,还讨论了连接排序优化、分布式成本模型以及多种查询优化方法,如静态优化、动态优化和混合优化。最后,提出了自适应查询处理的概念,旨在提高分布式查询的灵活性和效率。通过综合应用这些技术,可以有效提升分布式数据库的查询性能,降低通信和计算

gan8painter的博客 73

Servlet单例还是多例

1是单例还是多例 如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。 而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。 参考文章:https://www.cnblogs.com/softidea/p/7245899.html 2如何保证单例多线程的线程安全 1同步对线程共享数据的操作 ...

ld705454682的博客 5093

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

(转)Struts1,2,Servlet,Spring MVC单例多例 线程安全

Struts 1 单例,线程不安全,在请求的时候被第一次初始化 action中的service对象为何不会出现数据存储的错误,大体意思就是每一个用户发出一次请求后就有一个独立的线程与之绑定,且有一个对应的servlet实例,你在其之上做的操作只属于当前servlet实例,不会受其他servlet实例的影响,反之也不会影响其他线程的servlet实例。 Struts 2 默认多例,可以设置...

tamink2013的博客 160

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Servlet、页面跳转、乱码问题

servlet生命周期、servlet线程安全、页面跳转、转发和重定向、中文乱码问题

m0_67496588的博客 1169

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Struts1,2,Servlet,Spring MVC单例多例 线程安全

Struts 1 单例,线程不安全,在请求的时候被第一次初始化  action中的service对象为何不会出现数据存储的错误,大体意思就是每一个用户发出一次请求后就有一个独立的线程与之绑定,且有一个对应的servlet实例,你在其之上做的操作只属于当前servlet实例,不会受其他servlet实例的影响,反之也不会影响其他线程的servlet实例。 Struts 2 默认多

houbin0912的博客 739

Servlet其实是单例多线程

Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例); 2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的设置线程池中线程数目,初始化线程池通过web.xml,初始化每个参数值

xiaojiahao_kevin的博客 2万+

深入研究Servlet线程安全性问题

  摘 要:介绍了Servlet多线程机制,通过一个实例并结合Java 的内存模型说明引起Servlet线程不安全的原因,给出了保证Servlet线程安全的三种解决方案,并说明三种方案在实际开发中的取舍。  关键字:Servlet 线程安全 同步 Java内存模型 实例变量   Servlet/JSP技术和ASP、PHP等相比,由于其多线程运行而具有很高的执行效率。由于Servlet/JSP默认是

键者天行 7656

Servlet的单例多线程

Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例); 2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的&lt;Connector&gt;设置线程池中线程数目,初始化线程池...

腾龙凌九霄 197

Struts1,2,Servlet,Spring MVC单例多例 线程安全问题

本文章转载于:http://blog.youkuaiyun.com/zly9923218/article/details/51125881 Struts 1单例,线程不安全,在请求的时候被第一次初始化 action中的service对象为何不会出现数据存储的错误,大体意思就是每一个用户发出一次请求后就有一个独立的线程与之绑定,且有一个对应的servlet实例,你在其之上做的操作只属于当前ser

leo的专栏 556

servlet是单例的!!所以需要线程安全

 Servlet/JSP技术和ASP、PHP等相比,由于其多线程运行而具有很高的执行效率。由于Servlet/JSP默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题。然而,很多人编写Servlet/JSP程序时并没有注意到多线程安全性的问题,这往往造成编写的程序在少量用户访问时没有任何问题,而在并发用户上升到一定值时,就会经常出现一些莫明其妙的问题。  Servle

红蚂蚁(RED ANT) 3829

Servlet:单例多线程

Servlet:单例多线程 文章目录Servlet:单例多线程Servlet如何处理多个请求访问?JSP的中存在的多线程问题:问题问题一. 说明其Servlet容器如何采用单实例多线程的方式来处理请求问题二. 如何在开发中保证servlet是单实例多线程的方式来工作(也就是说如何开发线程安全的servelt)一. Servlet容器如何同时来处理多个请求二 如何开发线程安全的ServletServlet的生命周期:三个概念的理解:Servlet容器Servlet如何处理多个请求访问? Servlet容器默认

cx9977的博客 183

springboot中controller是单例的吗

这意味着即使Controller是单例的,多个线程也可以同时处理不同的请求,每个线程都在独立地执行请求的处理逻辑,而不会相互影响。当一个HTTP请求到达时,容器会从线程池中选择一个空闲线程来处理该请求,而不是为每个请求都创建一个新的线程。请求调度: 当多个HTTP请求同时到达时,Servlet容器会根据请求的特性(如优先级、等待时间等)进行调度,选择合适的线程来处理每个请求。在处理完一个请求后,线程会被重新放回线程池中,以供下一个请求使用,从而实现线程的复用和资源的高效利用。

qq_43422537的博客 1044

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

今天讲的是Servlet生命周期和Servlet的线程安全性问题。

annotation_PLZ的博客 1214
上一篇: 过滤器和拦截器
下一篇: Struts2和SpringMvc的区别
cashZhang27
博客等级 码龄10年 22粉丝 67原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值