Servlet与线程安全
Servlet與線程安全
先說結論,Servlet本身是單例的,線程安全的。但是如果引入共享變量,則可能會變得線程不安全。
1. 什么是線程安全
首先說明一下對線程安全的討論,哪種情況我們可以稱作線程安全?
《Java并發編程實戰》給出的定義:
當多個線程訪問某個類時,不管運行時環境采用何種調度方式,或者這些線程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么就稱這個類是線程安全的.
2. Servlet的生命周期,傳送門
3. Servlet的調用過程
Servlet體系結構是建立在Java多線程機制之上的,它的生命周期是由Web容器負責的。
- 1.客戶端通過發送請求給Tomcat,Tomcat發送客戶端的請求頁面給客戶端。
- 2.用戶對請求頁面進行相關操作后將頁面提交給Tomcat,Tomcat將其封裝成一個HttpRequest對象,然后對請求進行處理,。
- 3.Tomcat截獲請求,根據action屬性值查詢xml文件中對應的servlet-name,再根據servlet-name查詢到對應的java類(如果是第一次,Tomcat則會將servlet編譯成java類文件,所以如果servlet有很多的話第一次運行的時候程序會比較慢)。
- 4.Tomcat實例化查詢到的java類,注意該類只實例化一次。
- 5.調用java類對象的service()方法(如果不對service()方法進行重寫則根據提交的方式來決定執行doPost()方法還是doGet()方法)。
- 6.通過request對象取得客戶端傳過來的數據,對數據進行處理后通過response對象將處理結果寫回客戶端。
4. Servlet是線程安全的嗎?
從上面Servlet的調用過程可以看出,當客戶端第一次請求Servlet的時候,tomcat會根據web.xml配置文件實例化servlet,
當又有一個客戶端訪問該servlet的時候,不會再實例化該servlet,也就是多個線程在使用這個實例。
JSP/Servlet容器默認是采用單實例多線程(這是造成線程安全的主因)方式處理多個請求的,這種默認以多線程方式執行的設計可大大降低對系統的資源需求,提高系統的并發量及響應時間。
Servlet本身是無狀態的,一個無狀態的Servlet是絕對線程安全的,無狀態對象設計也是解決線程安全問題的一種有效手段。
所以,servlet是否線程安全是由它的實現來決定的,如果它內部的屬性或方法會被多個線程改變,它就是線程不安全的,反之,就是線程安全的。
下面這個示例來自《Java并發編程實戰》,在競態條件下存在線程不安全。
public class UnsafeCountingFactorizer implements Servlet{private long count=0;public long getCount(){return count;}@Overridepublic void service(ServletRequest req, ServletResponse resp)throws ServletException, IOException {BigInteger i=extractFromRequest();BigInteger[] factors=factor(i);++count;} }遞增操作count++并非是原子操作,它包含了三個獨立的操作:讀取count的值,將值加1,
然后將計算結果寫入count,這是一個“讀取-修改-寫入”的操作序列,并且其結果狀態依賴于之前的狀態。在執行時序不同的情況下,可能會產生錯誤。
5. 影響Servlet線程安全的因素
多線程下每個線程對局部變量都會有自己的一份copy,這樣對局部變量的修改只會影響到自己的copy而不會對別的線程產生影響,所以這是線程安全的。
但是對于實例變量來說,由于servlet在Tomcat中是以單例模式存在的,所有的線程共享實例變量。多個線程對共享資源的訪問就造成了線程不安全問題。
6. 如何保證Servlet的線程安全性?
7. 設計線程安全的Servlet
- 1.實現 SingleThreadModel 接口
該接口指定了系統如何處理對同一個Servlet的調用。如果一個Servlet被這個接口指定,那么在這個Servlet中的service方法將不會有兩個線程被同時執行,當然也就不存在線程安全的問題。但是,如果一個Servlet實現了SingleThreadModel接口,Servlet引擎將為每個新的請求創建一個單獨的Servlet實例,這將引起大量的系統開銷,在現在的Servlet開發中基本看不到SingleThreadModel的使用,這種方式了解即可,盡量避免使用。
public class XXXXX extends HttpServlet implements SingleThreadModel { //………… }- 2.同步對共享數據的操作
使用synchronized 關鍵字能保證一次只有一個線程可以訪問被保護的區段,可以通過同步塊操作來保證Servlet的線程安全。如果在程序中使用同步來保護要使用的共享的數據,也會使系統的性能大大下降。這是因為被同步的代碼塊在同一時刻只能有一個線程執行它,使得其同時處理客戶請求的吞吐量降低,而且很多客戶處于阻塞狀態。另外為保證主存內容和線程的工作內存中的數據的一致性,要頻繁地刷新緩存,這也會大大地影響系統的性能。所以在實際的開發中也應避免或最小化Servlet 中的同步代碼。
同步代碼:
public class XXXXXX extends HttpServlet {synchronized (this){XXXX} }- 3.避免使用實例變量
線程安全問題很大部分是由實例變量造成的,只要在Servlet里面的任何方法里面都不使用實例變量,那么該Servlet就是線程安全的。
在Servlet中避免使用實例變量是保證Servlet線程安全的最佳選擇。
Java 內存模型中,方法中的臨時變量是在棧上分配空間,而且每個線程都有自己私有的??臻g,所以它們不會影響線程的安全。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Servlet与线程安全的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 曝谷歌向三星支付80亿美元 买断三星手机
- 下一篇: 月销20万辆的奇瑞,还能否赶上新能源的末