Servlet 是单例还是多例?
面试中经常会被问到:Servlet 是单例还是多例,这是一个很基础的问题,主要考察面试者的基础是否扎实。还可以关联出一串更深层的问题来考察面试者的知识深度。
问:Servlet是单例还是多例?
答:单例
问:单例情况下在多个请求时是怎么处理的?
答:多线程,为每个请求分配一条线程
问:单例在并发情况下线程是否安全?
答:不安全
问:有没有办法解决?
答:有。。。。如果回答可以使用 ThreadLocal
来隔离每个请求的线程是最好的。接着更深的坑
问:ThreadLocal 的原理是什么?怎么实现的?
答:。。。。答不上来 OR 回答上来了。后面正等着呢
问:使用ThreadLocal与同步代码块,那个优劣?
问:多线程。。。。并发。。。。。
答:。。。。死穴。。。。OVER
Servlet是单例还是多例
查看 Servlet 定义
For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration. 如果 servlet 不是在分布式环境下(默认),servlet 容器必须使一个 servlet 实例对应一个 servlet 声明。 However, for a servlet implementing the SingleThreadModel(Deprecated) interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance. 然而,实现了 SingleThreadModel 接口的 Servlet,可以有多个实例。以处理繁重的请求, 并且序列化 request 到特定的 servlet 实例。 public interface SingleThreadModel Ensures that servlets handle only one request at a time.
因SingleThreadModel
已经声明为废弃,官方不建议使用,所以此种情况可以不予考虑。
Servlet 在多数情况下只有一个实例。但它并不是单例设计模式,即不是真正的单例。
查看 Tomcat 源码StandardWrapper
分析,当Web服务启动时(或客户端发送请求到服务器时),Servlet 就被加载并实例化了一次。
Servlet 是单例还是双例,与web.xml
文件里声明的 Servlet 次数有关。如果在web.xml
文件同一个 Servlet 映射了多个 <url-pattern>
,也会生成多例。
Servlet线程安全问题
Servlet 默认是单实例多线程来响应多个请求,这样就存在线程安全问题。如果有多个客户端同时请求JSP文件,则服务端会创建多个线程,每个请求对应一个线程。以下方法可解决线程安全问题:
使用同步块(或方法)来保护共享数据
同步来保护要使用的共享的数据,也会使系统的性能大大下降。
这是因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。
另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。
所以在实际的开发中也应避免或最小化 Servlet 中的同步代码;避免使用实例变量
在 Serlet 中避免使用实例变量是保证Servlet线程安全的最佳选择。从Java 内存模型也可以知道,方法中的临时变量是在栈上分配空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全。可以使用 ThreadLocal来隔离线程,实现线程内部变量独占
参考:Servlet线程安全问题——ThreadLocal模式可以使用 Lock 机制来实现
参考:JAVA多线程不安全问题解决方案
servlet单实例多线程模式