關於線程安全的涉及到概念以及分類

本文摘選自深入理解Java虛擬機。

方法一:互斥同步(Matual Exclusion & Synchronization)

互斥同步屬於一種悲觀的併發策略,總是認爲只要不去做正確的同步措施,就肯定會出現問題。無論共享數據是否出現競爭,它都要進行加鎖。故稱互斥同步的鎖爲悲觀鎖

①使用synchronized關鍵字。synchronized同步代碼塊對同一個線程來說是可重入的,不會出現自己把自己鎖死的問題。

 

②使用ReentrantLock重入鎖來實現同步。ReentrantLock與synchronized很相似。都是對同一個線程可重入。不過ReentrantLock增加了一些高級的功能:等待可中斷,可實現公平鎖。鎖可以綁定多個條件。

 

等待可中斷是指:當持有鎖的線程長期不釋放鎖的時候,正在等待的線程可以選擇放棄等待。改爲處理其他事情,可中斷特性對處理執行時間非常長的同步塊很有幫助。

 

公平鎖:多個線程在等待同一個鎖時,必須按照申請鎖的時間順序來依次獲得鎖。而非公平鎖不保證這一點:在鎖被釋放的時候,任何一個等待鎖的線程都有機會獲得鎖(通過競爭獲得鎖)。synchronized中的鎖是非公平鎖,ReentrantLock默認情況下也是非公平鎖,但是可以通過帶布爾值的構造函數來構造公平鎖。

 

鎖綁定多個條件是指一個ReentrantLock對象可以同時綁定多個Condition對象。在synchronized中,隱含着綁定一個對象,如果要是有多個對象關聯的時候,就要多加一個額外的鎖。ReentrantLock則不需要這樣做,只需要多次調用newCondition()方法即可。

 

方法二:非阻塞同步(又稱樂觀鎖)

 

互斥同步最主要的問題就是進行線程阻塞和喚醒所帶來的性能問題,因此這種同步也稱爲阻塞同步(Blocking Synchronization)。所以就有了一種不需把線程掛起的同步操作稱之爲非阻塞同步(Non-Blocking Synchronization):一種基於衝突檢測的樂觀併發策略,通俗了說,就是先進行操作,如果沒有其他線程爭用共享數據,那操作就成功了;如果共享數據有爭用,產生了衝突,那就採用其他的補償措施(最常見的的就是不斷重試,直到成功爲止)。在Java中常見的就是Compare-and Swap(CAS)指令。CAS指令有三個操作數,分別是內存位置的值(用V表示),舊的預期值A,新值B,CAS指令執行的時候,當且僅當V符合舊預期值A時,處理器用新值B更新V的值,否則 就不執行更新,無論是否更新V的值,都會返回V的舊值。

        CAS指令這個執行過程是個原子操作。其他的原子操作還有指令:測試並設置(Test- And-Set)、獲取並增加(Fetch-And-Increment)、交換(Swap)、加載鏈接/條件存儲(Load-Linked/Store-Conditional)。

方法三:無同步方案

 

如果一個方法本來就不涉及共享數據,那它自然就不需要任何同步措施去保證正確性,因此會有一些代碼天生就是線程安全的。

一類:可重入代碼(Reentrant Code):這種代碼叫做純代碼,可以在代碼執行的任何時刻去中斷它,轉而去執行另一段代碼,而在控制權返回後,原來的程序不會出現任何錯誤。判斷是否可重入性的原則:如果一個方法,他的返回結果是可以預測的,只要輸入了相同的數據,就能返回相同的結果,那它就滿足可重入性的要求。

 

二類:線程本地存儲(Thread Local Storage):如果一段代碼中所需要的數據必須與其他代碼共享數據。如果能保證這些共享數據的代碼在同一個線程中執行,那麼這些共享數據的可見範圍在同一個線程之內。這樣不需要同步也能保證線程之間不出現數據爭用的問題。在Java中,通過java.lang.ThreadLocal 類來實現線程本地存儲的功能。

發佈了30 篇原創文章 · 獲贊 27 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章