日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Servlet 单例多线程

發(fā)布時間:2025/3/15 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Servlet 单例多线程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Servlet 單例多線程


Servlet如何處理多個請求訪問?

Servlet容器默認是采用單實例多線程的方式處理多個請求的:

1.當web服務器啟動的時候(或客戶端發(fā)送請求到服務器時),Servlet就被加載并實例化(只存在一個Servlet實例);

2.容器初始化化Servlet主要就是讀取配置文件(例如tomcat,可以通過servlet.xml的<Connector>設置線程池中線程數(shù)目,初始化線程池通過web.xml,初始化每個參數(shù)值等等。

3.當請求到達時,Servlet容器通過調(diào)度線程(Dispatchaer Thread) 調(diào)度它管理下線程池中等待執(zhí)行的線程(Worker Thread)給請求者;

4.線程執(zhí)行Servlet的service方法;

5.請求結束,放回線程池,等待被調(diào)用;

(注意:避免使用實例變量(成員變量),因為如果存在成員變量,可能發(fā)生多線程同時訪問該資源時,都來操作它,照成數(shù)據(jù)的不一致,因此產(chǎn)生線程安全問題)


從上面可以看出:

第一:Servlet單實例,減少了產(chǎn)生servlet的開銷;

第二:通過線程池來響應多個請求,提高了請求的響應時間;

第三:Servlet容器并不關心到達的Servlet請求訪問的是否是同一個Servlet還是另一個Servlet,直接分配給它一個新的線程;如果是同一個Servlet的多個請求,那么Servlet的service方法將在多線程中并發(fā)的執(zhí)行;

第四:每一個請求由ServletRequest對象來接受請求,由ServletResponse對象來響應該請求;


Servlet/JSP技術和ASP、PHP等相比,由于其多線程運行而具有很高的執(zhí)行效率。由于Servlet/JSP默認是以多線程模式執(zhí)行的,所以,在編寫代碼時需要非常細致地考慮多線程的安全性問題。?


JSP的中存在的多線程問題:?

當客戶端第一次請求某一個JSP文件時,服務端把該JSP編譯成一個CLASS文件,并創(chuàng)建一個該類的實例,然后創(chuàng)建一個線程處理CLIENT端的請求。如果有多個客戶端同時請求該JSP文件,則服務端會創(chuàng)建多個線程。每個客戶端請求對應一個線程。以多線程方式執(zhí)行可大大降低對系統(tǒng)的資源需求,提高系統(tǒng)的并發(fā)量及響應時間.?


對JSP中可能用的的變量說明如下:?

實例變量: 實例變量是在堆中分配的,并被屬于該實例的所有線程共享,所以不是線程安全的.?

JSP系統(tǒng)提供的8個類變量?

JSP中用到的OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT是線程安全的(因為每個線程對應的request,respone對象都是不一樣的,不存在共享問題), APPLICATION在整個系統(tǒng)內(nèi)被使用,所以不是線程安全的.?


局部變量: 局部變量在堆棧中分配,因為每個線程都有它自己的堆??臻g,所以是線程安全的.?

靜態(tài)類: 靜態(tài)類不用被實例化,就可直接使用,也不是線程安全的.?


外部資源: 在程序中可能會有多個線程或進程同時操作同一個資源(如:多個線程或進程同時對一個文件進行寫操作).此時也要注意同步問題.?


使它以單線程方式執(zhí)行,這時,仍然只有一個實例,所有客戶端的請求以串行方式執(zhí)行。這樣會降低系統(tǒng)的性能?


問題?

問題一. 說明其Servlet容器如何采用單實例多線程的方式來處理請求?

問題二. 如何在開發(fā)中保證servlet是單實例多線程的方式來工作(也就是說如何開發(fā)線程安全的servelt)。?


一. Servlet容器如何同時來處理多個請求?


Java的內(nèi)存模型JMM(Java Memory Model)?

JMM主要是為了規(guī)定了線程和內(nèi)存之間的一些關系。根據(jù)JMM的設計,系統(tǒng)存在一個主內(nèi)存(Main Memory),Java中所有實例變量都儲存在主存中,對于所有線程都是共享的。每條線程都有自己的工作內(nèi)存(Working Memory),工作內(nèi)存由緩存和堆棧兩部分組成,緩存中保存的是主存中變量的拷貝,緩存可能并不總和主存同步,也就是緩存中變量的修改可能沒有立刻寫到主存中;堆棧中保存的是線程的局部變量,線程之間無法相互直接訪問堆棧中的變量。根據(jù)JMM,我們可以將論文中所討論的Servlet實例的內(nèi)存模型抽象為圖所示的模型。?


?


工作者線程Work Thread:執(zhí)行代碼的一組線程。?

調(diào)度線程Dispatcher Thread:每個線程都具有分配給它的線程優(yōu)先級,線程是根據(jù)優(yōu)先級調(diào)度執(zhí)行的。?


Servlet采用多線程來處理多個請求同時訪問。servlet依賴于一個線程池來服務請求。線程池實際上是一系列的工作者線程集合。Servlet使用一個調(diào)度線程來管理工作者線程。?


當容器收到一個Servlet請求,調(diào)度線程從線程池中選出一個工作者線程,將請求傳遞給該工作者線程,然后由該線程來執(zhí)行Servlet的service方法。當這個線程正在執(zhí)行的時候,容器收到另外一個請求,調(diào)度線程同樣從線程池中選出另一個工作者線程來服務新的請求,容器并不關心這個請求是否訪問的是同一個Servlet.當容器同時收到對同一個Servlet的多個請求的時候,那么這個Servlet的service()方法將在多線程中并發(fā)執(zhí)行。?

Servlet容器默認采用單實例多線程的方式來處理請求,這樣減少產(chǎn)生Servlet實例的開銷,提升了對請求的響應時間,對于Tomcat可以在server.xml中通過<Connector>元素設置線程池中線程的數(shù)目。?


就實現(xiàn)來說:?

調(diào)度者線程類所擔負的責任如其名字,該類的責任是調(diào)度線程,只需要利用自己的屬性完成自己的責任。所以該類是承擔了責任的,并且該類的責任又集中到唯一的單體對象中。而其他對象又依賴于該特定對象所承擔的責任,我們就需要得到該特定對象。那該類就是一個單例模式的實現(xiàn)了。?



注意:服務器可以使用多個實例來處理請求,代替單個實例的請求排隊帶來的效益問題。服務器創(chuàng)建一個Servlet類的多個Servlet實例組成的實例池,對于每個請求分配Servlet實例進行響應處理,之后放回到實例池中等待下此請求。這樣就造成并發(fā)訪問的問題。?

此時,局部變量(字段)也是安全的,但對于全局變量和共享數(shù)據(jù)是不安全的,需要進行同步處理。而對于這樣多實例的情況SingleThreadModel接口并不能解決并發(fā)訪問問題。 SingleThreadModel接口在servlet規(guī)范中已經(jīng)被廢棄了。


二 如何開發(fā)線程安全的Servlet?

  1、實現(xiàn) SingleThreadModel 接口?


  該接口指定了系統(tǒng)如何處理對同一個Servlet的調(diào)用。如果一個Servlet被這個接口指定,那么在這個Servlet中的service方法將不會有兩個線程被同時執(zhí)行,當然也就不存在線程安全的問題。這種方法只要將前面的Concurrent Test類的類頭定義更改為:?


Public class Concurrent Test extends HttpServlet implements SingleThreadModel {?

…………?

}?


  2、同步對共享數(shù)據(jù)的操作?


  使用synchronized 關鍵字能保證一次只有一個線程可以訪問被保護的區(qū)段,在本論文中的Servlet可以通過同步塊操作來保證線程的安全。同步后的代碼如下:?


…………?

Public class Concurrent Test extends HttpServlet { …………?

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

Synchronized (this){?

Output = response.getWriter ();?

Try {?

Thread. Sleep (5000);?

} Catch (Interrupted Exception e){}?

output.println("用戶名:"+Username+"<BR>");?

}?

}?

}?


  3、避免使用實例變量?


  本實例中的線程安全問題是由實例變量造成的,只要在Servlet里面的任何方法里面都不使用實例變量,那么該Servlet就是線程安全的。?


  修正上面的Servlet代碼,將實例變量改為局部變量實現(xiàn)同樣的功能,代碼如下:?


……?

Public class Concurrent Test extends HttpServlet {public void service (HttpServletRequest request, HttpServletResponse?

Response) throws ServletException, IOException {?

Print Writer output;?

String username;?

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

……?

}?

}?



 ** 對上面的三種方法進行測試,可以表明用它們都能設計出線程安全的Servlet程序。但是,如果一個Servlet實現(xiàn)了SingleThreadModel接口,Servlet引擎將為每個新的請求創(chuàng)建一個單獨的Servlet實例,這將引起大量的系統(tǒng)開銷。SingleThreadModel在Servlet2.4中已不再提倡使用;同樣如果在程序中使用同步來保護要使用的共享的數(shù)據(jù),也會使系統(tǒng)的性能大大下降。這是因為被同步的代碼塊在同一時刻只能有一個線程執(zhí)行它,使得其同時處理客戶請求的吞吐量降低,而且很多客戶處于阻塞狀態(tài)。另外為保證主存內(nèi)容和線程的工作內(nèi)存中的數(shù)據(jù)的一致性,要頻繁地刷新緩存,這也會大大地影響系統(tǒng)的性能。所以在實際的開發(fā)中也應避免或最小化 Servlet 中的同步代碼;在Serlet中避免使用實例變量是保證Servlet線程安全的最佳選擇。從Java 內(nèi)存模型也可以知道,方法中的臨時變量是在棧上分配空間,而且每個線程都有自己私有的??臻g,所以它們不會影響線程的安全。

更加詳細的說明:


1,變量的線程安全:這里的變量指字段和共享數(shù)據(jù)(如表單參數(shù)值)。?

a,將 參數(shù)變量 本地化。多線程并不共享局部變量.所以我們要盡可能的在servlet中使用局部變量。?

例如:String user = "";?

user = request.getParameter("user");?


b,使用同步塊Synchronized,防止可能異步調(diào)用的代碼塊。這意味著線程需要排隊處理。在使用同板塊的時候要盡可能的縮小同步代碼的范圍,不要直接在sevice方法和響應方法上使用同步,這樣會嚴重影響性能。?


2,屬性的線程安全:ServletContext,HttpSession,ServletRequest對象中屬性。?

ServletContext:(線程是不安全的)?

ServletContext是可以多線程同時讀/寫屬性的,線程是不安全的。要對屬性的讀寫進行同步處理或者進行深度Clone()。所以在Servlet上下文中盡可能少量保存會被修改(寫)的數(shù)據(jù),可以采取其他方式在多個Servlet中共享,比方我們可以使用單例模式來處理共享數(shù)據(jù)。?

HttpSession:(線程是不安全的)?

HttpSession對象在用戶會話期間存在,只能在處理屬于同一個Session的請求的線程中被訪問,因此Session對象的屬性訪問理論上是線程安全的。?

當用戶打開多個同屬于一個進程的瀏覽器窗口,在這些窗口的訪問屬于同一個Session,會出現(xiàn)多次請求,需要多個工作線程來處理請求,可能造成同時多線程讀寫屬性。這時我們需要對屬性的讀寫進行同步處理:使用同步塊Synchronized和使用讀/寫器來解決。?

ServletRequest:(線程是安全的)?

對于每一個請求,由一個工作線程來執(zhí)行,都會創(chuàng)建有一個新的ServletRequest對象,所以ServletRequest對象只能在一個線程中被訪問。ServletRequest是線程安全的。注意:ServletRequest對象在service方法的范圍內(nèi)是有效的,不要試圖在service方法結束后仍然保存請求對象的引用。?


3,使用同步的集合類:?

使用Vector代替ArrayList,使用Hashtable代替HashMap。?


4,不要在Servlet中創(chuàng)建自己的線程來完成某個功能。?

Servlet本身就是多線程的,在Servlet中再創(chuàng)建線程,將導致執(zhí)行情況復雜化,出現(xiàn)多線程安全問題。?


5,在多個servlet中對外部對象(比方文件)進行修改操作一定要加鎖,做到互斥的訪問。?


6,javax.servlet.SingleThreadModel接口是一個標識接口,如果一個Servlet實現(xiàn)了這個接口,那Servlet容器將保證在一個時刻僅有一個線程可以在給定的servlet實例的service方法中執(zhí)行。將其他所有請求進行排隊。?



PS:

Servlet并非只是單例的. 當container開始啟動,或是客戶端發(fā)出請求服務時,Container會按照容器的配置負責加載和實例化一個Servlet(也可以配置為多個,不過一般不這么干).不過一般來說一個servlet只會有一個實例。

1) Struts2的Action是原型,非單實例的;會對每一個請求,產(chǎn)生一個Action的實例來處理。?

2) Struts1的Action,Spring的Ioc容器管理的bean 默認是單實例的.?


Struts1 Action是單實例的,spring mvc的controller也是如此。因此開發(fā)時要求必須是線程安全的,因為僅有Action的一個實例來處理所有的請求。單例策略限制了Struts1 Action能作的事,并且要在開發(fā)時特別小心。Action資源必須是線程安全的或同步的。?

Spring的Ioc容器管理的bean 默認是單實例的。

Struts2 Action對象為每一個請求產(chǎn)生一個實例,因此沒有線程安全問題。(實際上,servlet容器給每個請求產(chǎn)生許多可丟棄的對象,并且不會導致性能和垃圾回收問題)。

當Spring管理Struts2的Action時,bean默認是單實例的,可以通過配置參數(shù)將其設置為原型。(scope="prototype )





Servlet的生命周期:


1. ? ? ?Servlet在web服務器啟動時被加載并實例化,容器運行其init方法初始化,請求到達時運行其service方法;


2. ? ? ?service運行請求對應的doXXX(doGet,doPost)方法;


3. ? ? ?服務器銷毀實例,運行其destory方法;


Servlet的生命周期由Servlet容器管理;


(三個概念的理解:


Servlet容器<Web容器<應用服務器?


Servlet容器的主要任務就是管理Servlet的生命周期;


Web容器也稱之為web服務器,主要任務就是管理和部署web應用的;


應用服務器的功能非常強大,不僅可以管理和部署web應用,也可以部署EJB應用,實現(xiàn)容器管理的事務等等。。。



Web服務器就是跟基于HTTP的請求打交道,而EJB容器更多是跟數(shù)據(jù)庫,事務管理等服務接口交互,所以應用服務器的功能是很多的。


常見的web服務器就是Tomcat,但Tomcat同樣也是Servlet服務器;


常見的應用服務器有WebLogic,WebSphere,但都是收費的;


沒有Servlet容器,可以用Web容器直接訪問靜態(tài)Html頁面,比如安裝了apache等;如果需要顯示Jsp/Servlet,就需要安裝一個Servlet容器;但是光有servlet容器也是不夠的,它需要被解析為html顯示,所以仍需要一個web容器;所以,我們常把web容器和Servlet容器視為一體,因為他們兩個容器都有對方的功能實現(xiàn)了,都沒有獨立的存在了,比如tomcat!




Servlet是如何處理多個請求同時訪問呢?


Servlet容器默認是采用單實例多線程的方式處理多個請求的:


1. ? ? ?當web服務器啟動的時候(或客戶端發(fā)送請求到服務器時),Servlet就被加載并實例化(只存在一個Servlet實例);


2. ? ? ?容器初始化Servlet。主要就是讀取配置文件(例如tomcat,可以通過servlet.xml的<Connector>設置線程池中線程數(shù)目,初始化線程池;通過web.xml,初始化每個參數(shù)值等等);


3. ? ? ?當請求到達時,Servlet容器通過調(diào)度線程(Dispatchaer Thread)調(diào)度它管理下的線程池中等待執(zhí)行的線程(Worker Thread)給請求者;


4. ? ? ?線程執(zhí)行Servlet的service方法;


5. ? ? ?請求結束,放回線程池,等到被調(diào)用;


從上面可以看出:


第一:Servlet單實例,減少了產(chǎn)生servlet的開銷;


第二:通過線程池來響應多個請求,提高了請求的響應時間;


第三:Servlet容器并不關心到達的Servlet請求訪問的是否是同一個Servlet還是另一個Servlet,直接分配給它一個新的線程;如果是同一個Servlet的多個請求,那么Servlet的service方法將在多線程中并發(fā)的執(zhí)行;


第四:每一個請求由ServletRequest對象來接受請求,由ServletResponse對象來響應該請求;



問題出現(xiàn)了:


同一個Servlet的的多個請求到來時,如果該Servlet中存在成員變量,可能發(fā)生多線程同時訪問該資源時,都來操作它,造成數(shù)據(jù)的不一致,因此產(chǎn)生線程安全問題。


解決:


1. ? ? ?實現(xiàn)SingleThreadModel接口


如果一個Servlet被這個接口指定,那么在這個Servlet中的service方法將不會有兩個線程被同時執(zhí)行,當然也就不存在線程安全的問題;


2. ? ? ?同步對共享數(shù)據(jù)的操作


使用synchronized關鍵字能保證一次只有一個線程可以訪問被保護的區(qū)段,Servlet可以通過同步塊操作來保證線程的安全。


ServletRequest對象是線程安全的,但是ServletContext和HttpSession不是線程安全的;


要使用同步的集合類:Vector代替ArrayList,HsahTable代替HashMap;


3. ? ? ?避免使用實例變量(成員變量)


線程安全問題是由實例變量造成的,只要在Servlet里面的任何方法里面都不使用實例變量,那么該Servlet就是線程安全的。(所有建議不要在servlet中定義成員變量,盡量用局部變量代替)



對上面的三種方法進行測試,可以表明用它們都能設計出線程安全的Servlet程序。但是,如果一個Servlet實現(xiàn)了SingleThreadModel接口,Servlet引擎將為每個新的請求創(chuàng)建一個單獨的Servlet實例,這將引起大量的系統(tǒng)開銷。SingleThreadModel在Servlet2.4中已不再提倡使用;同樣如果在程序中使用同步來保護要使用的共享的數(shù)據(jù),也會使系統(tǒng)的性能大大下降。這是因為被同步的代碼塊在同一時刻只能有一個線程執(zhí)行它,使得其同時處理客戶請求的吞吐量降低,而且很多客戶處于阻塞狀態(tài)。另外為保證主存內(nèi)容和線程的工作內(nèi)存中的數(shù)據(jù)的一致性,要頻繁地刷新緩存,這也會大大地影響系統(tǒng)的性能。所以在實際的開發(fā)中也應避免或最小化Servlet中的同步代碼;在Serlet中避免使用實例變量是保證Servlet線程安全的最佳選擇。從Java內(nèi)存模型也可以知道,方法中的臨時變量是在棧上分配空間,而且每個線程都有自己私有的棧空間,所以它們不會影響線程的安全。


 Servlet的線程安全問題只有在大量的并發(fā)訪問時才會顯現(xiàn)出來,并且很難發(fā)現(xiàn),因此在編寫Servlet程序時要特別注意。線程安全問題主要是由實例變量造成的,因此在Servlet中應避免使用實例變量。如果應用程序設計無法避免使用實例變量,那么使用同步來保護要使用的實例變量,但為保證系統(tǒng)的最佳性能,應該同步可用性最小的代碼路徑。


轉(zhuǎn)載于:https://my.oschina.net/liangzhenghui/blog/506141

總結

以上是生活随笔為你收集整理的Servlet 单例多线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。