1. 程式人生 > >深入理解Java虛擬機器讀書筆記9----執行緒完全與鎖優化

深入理解Java虛擬機器讀書筆記9----執行緒完全與鎖優化

九 執行緒完全與鎖優化   1 Java語言中的執行緒完全         ---執行緒安全:當多個執行緒訪問一個物件時,如果不用考慮這些執行緒在執行時環境下的排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲得正確的結果,那這個物件是執行緒安全的。         ---按照執行緒安全的“安全程度”,可以將Java語言中的共享資料分為以下5類:不可變、絕對執行緒安全、相對執行緒安全、執行緒相容和執行緒對立。      (1)不可變
            ---不可變的物件一定是執行緒安全的。             ---Java語言中,如果共享資料是基本資料型別,那麼只要在定義時使用final關鍵字修飾它就可以保證它是不可變的;如果共享資料是一個物件,則需要保證物件的行為不會對其狀態產生任何影響才行。             ---保證物件行為不會影響自己狀態的途徑有很多,如把物件中帶有狀態的變數都宣告為final,例如java.lang.Integer。             ---Java API中符合不可變要求的型別常用的有:java.lang.String、列舉類、java.lang.Number的部分子類(如Long、Double、BigInteger、BigDecimal等)。     (2)絕對執行緒安全
            ---需要達到“不管執行時環境如何,呼叫者都不需要任何額外的同步措施。             ---在Java API中標註自己是執行緒安全的類,大多數都不是絕對執行緒安全的,如java.util.Vector。     (3)相對執行緒安全             ---對這個物件單獨的操作是執行緒安全的,在呼叫的時候不需要做額外的同步措施;但是對於一些特定順序的連續呼叫,就可能需要在呼叫端使用額外的同步手段來保證呼叫的正確性。             ---Java語言中,大部分的執行緒安全類都屬於這種型別,如Vector、HashTable等。     (4)執行緒相容
            ---執行緒相容是指物件本身不是執行緒安全的,但是可以通過在呼叫端正確地使用同步手段來保證物件在併發環境中可以安全地使用。             ---Java API中大部分的類都是屬於執行緒相容的。     (5)執行緒對立             ---執行緒對立是指無論呼叫端是否採取了同步措施,都無法在多執行緒環境中併發使用的程式碼。             ---例子:Thread類的suspend()和resume()方法。   2 執行緒安全的實現方法     (1)互斥同步             ---同步是指在多個執行緒併發訪問共享資料時,保證共享資料在同一個時刻只被一個執行緒使用。             ---互斥是實現同步的一種手段。             ---互斥的實現方式:臨界區、互斥量、訊號量。             ---在Java中,最基本的互斥同步手段就是synchronized關鍵字,synchronized關鍵字經過編譯後,會在同步塊前後分別形成monitorenter和monitorexit位元組碼指令。             ---monitorenter和monitorexit都需要一個reference型別的引數來明確指定鎖定和解鎖的物件。如果Java程式中synchronized明確指定了物件引數,那就是這個·物件的reference;如果沒有明確指定,那就根據synchronized修飾的是例項方法還是類方法,去取對應的物件例項或Class物件來作為鎖物件。             ---兩點需要注意:                 · synchronized同步塊對同一條執行緒來說是可重入的不會出現自己把自己鎖死的問題;                 · 同步塊在已進入的執行緒執行完之前,會阻塞後面其他執行緒的進入。             ---還可以使用j'ava.util.concurrent包中的重入鎖(ReentrantLock)來實現同步,相比synchronized,ReentrantLock增加了一些高階功能,主要有:                 · 等待可中斷:當持有鎖的執行緒長期不釋放鎖的時候,正在等待的執行緒可以選擇放棄等待,改為處理其他事情。                 · 可實現公平鎖:公平鎖是指多個執行緒在等待同一個鎖時,必須按照申請鎖的時間順序來依次獲得鎖。synchronized中的鎖是非公平的,ReentrantLock中的鎖預設情況下也是非公平的。                 · 鎖可以繫結多個條件:一個ReentrantLock物件可以同時繫結多個Condition物件。             ---互斥同步主要問題:進行執行緒阻塞和喚醒都需要作業系統來幫忙完成,需要在使用者態和核心態之間進行切換,耗時較長。             ---互斥同步屬於一種悲觀的併發策略。無論共享資料是否真的會出現競爭,它都要進行加鎖、使用者態核心態轉換、維護鎖計數器和檢查是否有被阻塞的執行緒需要喚醒等操作。     (2)非阻塞同步             ---非阻塞同步是一種基於衝突的樂觀併發策略。採取先進行操作,如果沒有其他執行緒爭用共享資料,那操作就成功了;如果共享資料有爭用,產生了衝突,那就再採取其他的補償措施。             ---為了保證操作和衝突檢測兩個步驟具備原子性。常用的處理器指令有:                 · 測試並設定(Test-and-Set)                 · 獲取並增加(Fetch-and-Increment)                 · 交換(Swap)                 · 比較並交換(Compare-and-Swap, CAS)                 · 載入連結/條件儲存(Load-Linked/Store-Conditional)   3 鎖優化     (1)自旋鎖和自適應自旋             ---自旋鎖:如果物理機器有一個以上的處理器,能讓兩個或以上的執行緒同時並行執行,可以讓後面請求鎖的那個執行緒”稍等一下“,但不放棄處理器的執行時間,看看持有鎖的執行緒是否很快就會釋放鎖。為了讓執行緒等待,只需讓執行緒執行一個忙迴圈(自旋)。             ---適用場景:共享資料的鎖定狀態只會持續很短的一段時間。             ---自適應自旋鎖:自旋的時間不再固定,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定。     (2)鎖消除             ---鎖消除是指虛擬機器即時編譯器在執行時,對一些程式碼上要求同步,但是被檢測到不可能存在共享資料競爭的鎖進行消除。             ---鎖消除的主要判定依據來源於逃逸分析的資料支援:如果判斷在一段程式碼中,堆上的所有資料都不會逃逸出去從而被其他執行緒訪問到,那就可以把它們當作棧上資料對待,認為它們是私有的,從而無需同步加鎖。     (3)鎖粗化             ---如果虛擬機器探測到有一串零碎的的操作都對同一個物件加鎖,將會把加鎖同步的範圍擴充套件(粗化)到整個操作序列的外部。     (4)輕量級鎖             ---”重量級“鎖:使用作業系統互斥量來實現的傳統鎖。             ---如果沒有競爭,輕量級鎖使用CAS操作避免了使用互斥量的開銷;但如果存在鎖競爭,除了互斥量的開銷外,還額外發生了CAS操作,比傳統的重量級鎖更慢。             ---原理:                 · 加鎖過程:在程式碼進入同步塊的時候,如果此同步物件沒有被鎖定,虛擬機器首先將在當前執行緒的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於儲存鎖物件目前的Mark Word的拷貝。然後,虛擬機器將使用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指標。如果更新成功了,那麼這個執行緒就擁有了該物件的鎖,並且物件Mark Word的鎖標誌位將轉變為”00“,即表示此物件處於輕量級鎖定狀態。如果更新失敗了,虛擬機器首先會檢查物件的Mark Word是否指向當前執行緒的棧幀,如果是則說明當前執行緒已經擁有了這個物件的鎖,可以直接進入同步塊繼續執行;否則說明這個鎖物件已經被其他執行緒搶佔了。如果有兩個以上的執行緒爭用同一個鎖,那麼輕量級鎖將膨脹為重量級鎖,鎖標誌的狀態值變為”10“。                 · 解鎖過程:如果物件的Mark Word仍然指向執行緒的鎖記錄, 那就用CAS操作把物件當前的Mark Word和執行緒中複製的拷貝替換回來。如果替換成功,則解鎖成功;如果替換失敗,說明有其他執行緒嘗試過獲取該鎖,那就要在釋放鎖的同時,喚醒被掛起的執行緒。     (5)偏向鎖             ---目的是消除資料在無競爭情況下的同步原語,進一步提高程式的執行效能。             ---鎖會偏向於第一個獲得它的執行緒,如果在接下來的執行過程中,該鎖沒有被其他的執行緒獲取,則持有偏向鎖的執行緒將永遠不需要再進行同步。             ---原理:                 · 當鎖物件第一次被執行緒獲取的時候,虛擬機器將會把物件頭中的標誌位設為”01“,即偏向模式。同時使用CAS操作把獲取到這個鎖的執行緒的ID記錄在物件的Mark Word中,如果CAS操作成功,持有偏向鎖的執行緒以後每次進入這個鎖相關的同步塊時,虛擬機器都可以不再進行任何同步操作。                 · 當有另外一個執行緒去嘗試獲取這個鎖時,偏向模式宣告結束。根據鎖物件目前是否處於被鎖定的狀態,撤銷偏向恢復到未鎖定或輕量級鎖定的狀態。             ---偏向鎖、輕量級鎖的狀態轉化及物件Mark Word的關係如下圖所示: