一、前言
在前一篇文章中分析filterChain的线程安全问题时讲到了真正需要考虑线程安全问题与复用的场景是Request与Servlet的实例,本文重点分析一下Servlet实例的线程安全问题与池化复用机制。
二、正文
1. 什么情况下Servlet实例会发生线程安全问题?
直接给答案:
(1)多个线程尝试同时修改(未做synchronized/lock/condition等同步处理)同一个实例的非静态成员变量(非Atomic类型)时有可能发生线程安全问题;
(2)多个线程尝试同时修改(未做synchronized/lock/condition等同步处理)同一个类的静态成员变量(非Atomic类型)时有可能发生线程安全问题;
以下情况不会发生线程安全问题:
(1)多个线程,但是每个线程有一个实例;
(2)多个线程,同一实例,但不同时修改实例成员变量,只做成员变量无关的可重入方法调用,因为方法调用时使用到的JVM内存区域(栈)是线程私有的;
(3)多个线程,同一实例,同时修改实例成员变量,但是使用了同步机制(synchronized/lock/condition);
2. StandardWrapper对Servlet实例线程安全问题的处理
先给出分析得到的结论,后续再讲分析过程:
主要过程在StandardWrapper类的allocate方法中体现;
(1)对于实现了SingleThreadModel接口的Servlet,会采用池化复用机制
容器维护了一个instancePool的栈用来保存创建出的实例,分配实例时锁定instancePool对象,轮询已分配实例数量与现有实例数量的关系,若现有实例不足则创建,直至达最大实例数,达最大还不足则等待其他线程通过deallocate释放实例(也需锁定instancePool)重新进入栈中,当现有实例数超过已分配实例数时,则从栈中弹出一个实例用于分配;
(2)对于未实现SingleThreadModel接口的Servlet
采用双检锁的机制创建出Servlet单例返回即可;
(3)需要注意的地方
创建第一个实例之前,是不知道Servlet是否实现了SingleThreadModel接口的,因此在第一个实例创建出来之后需要进一步判断是否为STM模式,根据情况确定是直接返回还是需要将其放入实例池中。
3. StandardWrapper源码分析
还是首先给出类关系图:
前面的文章中已经提到过LifecycleMBeanBase是与JMX相关的内容,所以暂时忽略,那么需要重点分析的就是几个接口、ContainerBase与StandardWrapper;
首先来看几个接口里面都规定了些什么:
(1)Notification与NotificationEmitter:看到第一句话“Interface implemented by an MBean that emits Notifications.”就决定不再往下看了,是与JMX相关的东西,暂不考虑,毕竟分析还是得抓重点;
(2)ServletConfig:表征servlet配置对象,用于container与servlet之间传递信息,定义了四个方法,分别为getServletName、getServletContext、getInitParameter、getInitParameterNames;
(3)Container:表征容器对象,容器是能够执行请求得到响应的对象,可以选择性地支持一条Pipeline(包含多个Valve(实际处理请求的实体)),支持一些组件(Loader、Logger、Manager、Realm、Resources),容器分为四种级别,由高到低分别为Engine、Host、Context、Wrapper,可以嵌套,可以有父容器和子容器集合,接口中规定了一些容器相关属性,Pipeline,监听器,上下文,集群以及父子容器的获取方法;
(4)Wrapper:层级最低的容器,不允许有子容器,一个Wrapper表示了Web应用中一个独立的Servlet,实现了对SingleThreadModel(后简称STM)的处理,管理Servlet实例的生命周期,定义的最主要的方法是allocate与deallocate,实现Servlet实例的分配与回收。
其次再看ContainerBase类:
ContainerBase是对Container接口的基础实现,提供一些容器具备的基础功能,对子类提供抽象方法invoke,在其内部定义了一条Pipeline用于组织调用多个Valve的invoke方法,通过Listener的方式实现了一些容器事件的通知,实现了对父子容器与集群以及Realm(用于验证用户角色的安全领域实体,主要有authenticate/hasRole等方法,是一个只读facade,一般被绑定于Context或级别更高的容器)的处理,实现了对自身及子容器后台处理方法的调用;
最后来看StandardWrapper类:
源码总共1800多行,全部一股脑分析的话有点冗余,这里只挑其中最重要的成员变量与方法进行分析,采用直接在源码中加注释的方式:
(1)成员变量:
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper, NotificationEmitter {
//日志打印工具对象
private static final Log log = LogFactory.getLog(StandardWrapper.class);
//Servlet处理的默认请求方法
protected static final String[] DEFAULT_SERVLET_METHODS = new String[] {
"GET", "HEAD", "POST" };
// ----------------------------------------------------------- Constructors
/**
* Create a new StandardWrapper component with the default basic Valve.
*/
public StandardWrapper() {
super();
//把Pipeline的最后一个标准容器阀给new出来
swValve=new StandardWrapperValve();
pipeline.setBasic(swValve);
//JMX通知相关组件
broadcaster = new NotificationBroadcasterSupport();
}
// ----------------------------------------------------- Instance Variables
/**
* The date and time at which this servlet will become available (in
* milliseconds since the epoch), or zero if the servlet is available.
* If this value equals Long.MAX_VALUE, the unavailability of this
* servlet is considered permanent.
*/
//在什么时间这个Servlet会变得可用,Long.MAX_VALUE表示永久不可用
protected long available = 0L;
/**
* The broadcaster that sends j2ee notifications.
*/
protected final NotificationBroadcasterSupport broadcaster;
/**
* The count of allocations that are currently active (even if they
* are for the same instance, as will be true on a non-STM servlet).
*/
//当前活跃的Servlet对象分配数量,使用原子量来保证线程安全
protected final AtomicInteger countAllocated = new AtomicInteger(0);
/**
* The facade associated with this wrapper.
*/
//外观模式:对外暴露的容器外观,用于传递容器中包含的ServletConfig参数而隐藏其他容器细节
protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);
/**
* The (single) possibly uninitialized instance of this servlet.
*/
//线程间可见Servlet单例
protected volatile Servlet instance = null;
/**
* Flag that indicates if this instance has been initialized
*/
//标志Servlet单例是否被成功初始化的标志位
protected volatile boolean instanceInitialized = false;
/**
* The load-on-startup order value (negative value means load on
* first call) for this servlet.
*/
//
protected int loadOnStartup = -1;
/**
* Mappings associated with the wrapper.
*/
protected final ArrayList<String> mappings = new ArrayList<>();
/**
* The initialization parameters for this servlet, keyed by
* parameter name.
*/
//Servlet的初始化参数
protected HashMap<String, String> parameters = new HashMap<>();
/**
* The security role references for this servlet, keyed by role name
* used in the servlet. The corresponding value is the role name of
* the web application itself.
*/
//Servlet的安全角色引用,key为servlet中使用的role name,value为web应用自身的role name
protected HashMap<String, String> references = new HashMap<>();
/**
* The run-as identity for this servlet.
*/
//作为***运行的标识
protected String runAs = null;
/**
* The notification sequence number.
*/
//JMX通知相关的序列号
protected long sequenceNumber = 0;
/**
* The fully qualified servlet class name for this servlet.
*/
//Servlet类的全限定名
protected String servletClass = null;
/**
* Does this servlet implement the SingleThreadModel interface?
*/
//线程间可见的Servlet是否支持singleThreadModel的标识
protected volatile boolean singleThreadModel = false;
/**
* Are we unloading our servlet instance at the moment?
*/
//线程间可见标识:当前是否正在卸载Servlet实例
protected volatile boolean unloading = false;
/**
* Maximum number of STM instances.
*/
//Servlet使用STM模式时,Servlet实例的最大数量
protected int maxInstances = 20;
/**
* Number of instances currently loaded for a STM servlet.
*/
//STM模式下:当前已经加载的Servlet实例数量
protected int nInstances = 0;
/**
* Stack containing the STM instances.
*/
//Servlet实例池,采用栈实现
protected Stack<Servlet> instancePool = null;
/**
* Wait time for servlet unload in ms.
*/
//等待Servlet卸载所需要的时间(ms)
protected long unloadDelay = 2000;
/**
* True if this StandardWrapper is for the JspServlet
*/
//是否为JSPServlet
protected boolean isJspServlet;
/**
* The ObjectName of the JSP monitoring mbean
*/
//JSP监控的MBean
protected ObjectName jspMonitorON;
/**
* Should we swallow System.out
*/
//是否覆盖System.out
protected boolean swallowOutput = false;
// To support jmx attributes
//标准容器阀
protected StandardWrapperValve swValve;
//Servlet实例加载时间
protected long loadTime=0;
//Servlet类加载时间
protected int classLoadTime=0;
/**
* Multipart config
*/
//多部分配置元素实例
protected MultipartConfigElement multipartConfigElement = null;
/**
* Async support
*/
//是否支持异步
protected boolean asyncSupported = false;
/**
* Enabled
*/
//容器是否可用
protected boolean enabled = true;
//是否要求扫描Servlet的安全注解
protected volatile boolean servletSecurityAnnotationScanRequired = false;
//是否可被重写
private boolean overridable = false;
/**
* Static class array used when the SecurityManager is turned on and
* <code>Servlet.init</code> is invoked.
*/
//当SecurityManager开启且Servlet.init方法调用时会用到的静态class数组
protected static Class<?>[] classType = new Class[]{
ServletConfig.class};
//三个读写锁:当且仅当只有读锁时可重入