Servlet到底是單例還是多例你了解嗎?


為一個Java Web開發者,你一定了解和學習過Servlet。或許還曾在面試中被問到過Servelt是單例還是多例這個問題。

遇到這個問題,你是否曾深入了解過,還是百度或者Google了一下,得到答案就OK了呢?

我們今天從Servlet規范及Tomcat源碼實現的角度,分析下這個問題。

在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,如果聲明多次,也會生成多個實例。



作者:侯樹成
鏈接:http://www.jianshu.com/p/caba33d8f3fd
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM