並發(concurrency)一個並不陌生的詞,簡單來說,就是cpu在同一時刻執行多個任務。
而Java並發則由多線程實現的。
在jvm的世界里,線程就像不相干的平行空間,串行在虛擬機中。(當然這是比較籠統的說法,線程之間是可以交互的,他們也不一定是串行。)
多線程的存在就是壓榨cpu,提高程序性能,還能減少一定的設計復雜度(用現實的時間思維設計程序)。
這么說來似乎線程就是傳說中的銀彈了,可事實告訴我們真正的銀彈並不存在
死鎖和臟數據就是典型的線程安全問題。
簡單來說,線程安全就是: 在多線程環境中,能永遠保證程序的正確性
只有存在共享數據時才需要考慮線程安全問題
其中, 方法區和堆就是主要的線程共享區域。那么就是說共享對象只可能是類的屬性域或靜態域
那么,什么才是線程安全的呢?
如果你在Google搜索就會出現許多像這樣的“定義”:
- 線程安全的代碼是多個線程同時執行也能工作的代碼
- 如果一段代碼可以保證多個線程訪問的時候正確操作共享數據,那么它是線程安全的
你有沒有覺得上面的定義似乎沒有傳達任何有意義的信息,反而會讓你更混亂。 雖然這些定義都是沒錯的,但事實是沒有提供任何實際的幫助和觀點,
我們如何區分線程安全類和不安全類呢?線程 “安全” 是什么意思?
線程安全任何合理定義的核心都是在講線程安全的正確性。因此,在了解線程安全之前,我們首先應該了解這個 “正確性”
正確性意味着類的行為符合規范
你當然認可這個定義,一個好的類規范會在任何時間都可以獲得類的狀態信息,並且可以對它進行操作。 通常我們沒有一個准則來定義自己的類是否符合規范,怎么知道它們是正確的? 當然不知道,但我們依然可以使用它,一旦我們能夠讓自己的 “代碼有效”。 這種 “代碼自信” 與我們接近正確性有關
只要把“正確性”定義為我們可以理解的內容,現在我們可以用一種非專業的解釋來定義它:當一個類被多個線程進行訪問並且正確運行,它就是線程安全的
當多個線程訪問某各類時,不管運行時環境采用何種調度方式或者這些線程將如何交替執行,並且在主調代碼中不需要任何額外的同步或者協同,這個類都能表現出正確的行為,那么就稱這個類是線程安全的。
示例:無狀態的Servlet
一個很好的線程安全的例子是沒有字段和引用的 Servlet 類,因為沒有字段和引用所以是 無狀態 的
public class StatelessFactorizer implements Servlet{ public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = factor(i); encodeIntoResponse(resp, factors); } }
特定計算的臨時狀態被存儲在線程堆棧的局部變量表中,只能被當前執行的線程訪問。 線程A訪問 StatelessFactorizer
不會影響線程B訪問同一個 StatelessFactorizer
; 因為兩個線程不共享狀態,就像是在訪問不同的實例。 由於訪問無狀態的對象時線程的操作不會影響其他線程中操作的正確性,因此無狀態對象是線程安全的
多線程假想:眾所周知,Servlet是被設計為單實例,在請求進入tomcat后,由Connector建立連接,再講請求分發給內部線程池中的Processor,
此時Servlet就處於一個多線程環境。即如果存在幾個請求同時訪問某個servlet,就可能會有幾個線程同時訪問該servlet對象。
多線程模型下的並發訪問策略
1、java監視器模式。 一直使用某一對象的鎖來保護某狀態。
2、線程安全委托。 將類的線程安全性委托給某個或多個線程安全的狀態變量(注意多個時,這些變量必須是彼此獨立,且不存在相關聯的不變性條件)
解決機制
1、加鎖:
a、鎖能使其保護的代碼以串行的形式來訪問,當給一個復合操作加鎖后,能使其成為原子操作。一種錯誤的思想是只要對寫數據的方法加鎖,其實這是錯的,對數據進行操作的所有方法都需加鎖,不管是讀還是寫
b、加鎖時需要考慮性能問題,不能總是一味地給整個方法加鎖synchronized就了事了,應該將方法中不影響共享狀態且執行時間比較長的代碼分離出去
c、加鎖的含義不僅僅局限於互斥,還包括可見性。為了確保所有線程都能看見最新值,讀操作和寫操作必須使用同樣的鎖對象
2、不共享狀態:
無狀態對象: 無狀態對象一定是線程安全的,因為不會影響到其他線程
線程關閉: 僅在單線程環境下使用
3、不可變對象:
可以使用final修飾的對象保證線程安全,由於final修飾的引用型變量(除String外)不可變是指引用不可變,但其指向的對象是可變的,所以此類必須安全發布,也即不能對外提供可以修改final對象的接口