背景
Servlet/JSP技術(shù)和ASP、PHP等相比,由于其多線程運行而具有很高的執(zhí)行效率。由于Servlet/JSP默認是以多線程模式執(zhí)行的,所以,在編寫代碼時需要非常細致地考慮多線程的安全性問題。然而,很多人編寫Servlet/JSP程序時并沒有注意到多線程安全性的問題,這往往造成編寫 的程序在少量用戶訪問時沒有任何問題,而在并發(fā)用戶上升到一定值時,就會經(jīng)常出現(xiàn)一些莫明其妙的問題。
線程安全:
一個類或者程序所提供的接口對于線程來說是原子操作或者多個線程之間的切換不會導致該接口的執(zhí)行結(jié)果存在二義性,也就是說我們不用考慮同步的問題。若每個線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;通俗的講就是:在多線程環(huán)境下,多個線程對共享變量進行操作后,變量的改變后的值和預期是一樣的。若有多個線程同時執(zhí)行寫操作,一般都需要考慮線程安全,否則的話就可能影響線程安全。
Servlet線程安全問題
由于在Web容器中,為了減少每一次請求Servlet的開銷的時候,我們通過將Servlet以單利的模式創(chuàng)建,而這個時候由于存在共享變量所以,會出現(xiàn)線程安全的問題。
如果解決線程安全:
?1、實現(xiàn) SingleThreadModel 接口
該接口指定了系統(tǒng)如何處理對同一個Servlet的調(diào)用。如果一個Servlet被這個接口指定,那么在這個Servlet中的service方法將不會有兩個線程被同時執(zhí)行,當然也就不存在線程安全的問題(注意:SingleThreadModel不會解決所有的線程安全隱患。?例如,會話屬性和靜態(tài)變量仍然可以被多線程的多請求同時訪問,即便使用了SingleThreadModel servlet。建議開發(fā)人員應當采取其他手段來解決這些問題,而不是實現(xiàn)該接口,比如 避免實例變量的使用或者在訪問資源時同步代碼塊。該接口在Servlet API 2.4中將不推薦使用。)
?2、同步對共享數(shù)據(jù)的操作
? ? ? ?使用synchronized關(guān)鍵字,通過對象的鎖機制保證同一時間只有一個線程訪問變量。當然,這個被用作“鎖機制”的變量是多個線程共享的。提供一份變量,讓不同的線程排隊訪問。(以時間換空間)
?3、避免使用實例變量
? ? 本實例中的線程安全問題是由實例變量造成的,只要在Servlet里面的任何方法里面都不使用實例變量,那么該Servlet就是線程安全的。
?4、使用ThreadLocal模式
? ?java.lang.ThreadLocal,提供了一種解決多線程并發(fā)問題的方案,該類在維護變量時,實際使用了當前線程中的一個叫做ThreadLocalMap的獨立副本,每個線程可以獨立修改屬于自己的副本而不會互相影響,從而隔離了線程和線程,避免了線程訪問實例變量發(fā)生沖突的問題。ThreadLocal本身并不是一個變量,而是通過操作當前線程的一個內(nèi)部變量來達到與其他線程隔離的目的。從命名也可以看出操作的對象是線程的一個本地變量。
小結(jié)
Servlet的線程安全問題只有在大量的并發(fā)訪問時才會顯現(xiàn)出來,并且很難發(fā)現(xiàn),因此在編寫Servlet程序時要特別注意。線程安全問題主要是由實例變量造成的,因此在Servlet中應避免使用實例變量。如果應用程序設(shè)計無法避免使用實例變量,那么使用同步來保護要使用的實例變量,但為保證系統(tǒng)的最佳性能,應該同步可用性最小的代碼路徑。