java虛擬機筆記(十三)線程安全的實現方式

線程安全:
多個線程存在共享數據訪問的場景下 ,線程安全可以分爲五個級別
1.不可變
不可變對象是線程安全的,被final修飾的基本類型變量
java中的BigInteger 和BigDecimal Long Double String 等
不可變對象 需要保證對象的行爲 不影響對象的狀態 。
比如說String的subString()方法 返回的是一個新的對象,而非改變它本身的值。

2.絕對線程安全
不管運行環境如何,調用者都不需要任何同步措施。

java api中大多數標誌線程安全的類 都不是絕對線程安全
比如說Vector的get()和remove()都被synchronized修飾,都是單個操作都是線程安全的。
但是兩個線程安全的操作組合在一起,就不一定了,已經不是原子性的了。
比如說一個線程在循環get,同時有一個線程在remove()元素,最終就有可能出現get報數組越界
因爲元素被移除了。

3.相對線程安全
大部分API都屬於這部分,保證單次調用是線程安全的,但是無法保證多個操作連續調用也是。
比如說vector hashtable Collections.synchronizedCollection() 等

4.線程兼容
普通的對象就是屬於這層,本身不是線程安全,但是在使用的時候需要通過協調同步達到線程安全。

5.線程對立
不管怎麼樣,都不能在多線程環境下執行,java中極少,比如說Thread.suspend()和Thread.resume() 一個掛起,一個恢復
,如果兩個線程持有同一個thread對象,不管是否同步, 併發執行這兩個操作都可能會死鎖,比如說要suspend的線程 就是要resume的線程, 這兩個方法已經不推薦使用了。

線程安全的實現方式:
1.互斥同步
多個線程併發訪問同一個共享變量時,確保同一時刻只有一個線程能訪問,這是同步。 實現同步的方式是互斥,
互斥有多種,臨界區,互斥量,信號量等 都是互斥的實現方式。

java裏面 synchronize 就是最基本的實現互斥同步的方式。
被synchronize修飾的方法或者代碼塊,在編譯成字節碼之後,會在邊界分別加上monitorenter和monitorexit 指令,
這兩個指令都需要一個refrence類型的參數來指明要鎖定和解鎖的對象。
如果沒有指定refrence類型的參數,那麼就根據當前修飾的方法是實例方法還是類方法,來決定要鎖定和解鎖的對象是這個方法所屬的實例對象還是Class對象。

執行monitorenter指令時候,嘗試去獲取對象的鎖,如果獲取失敗,就阻塞等待,如果獲取成功(對象未加鎖,或者當前線程已經持有了這個對象的鎖),那麼就把鎖的計數器+1,
執行monitorexit指令的時候,將鎖的計數器-1。 如果計數器爲0,對象的鎖就釋放了。

同一線程可以反覆進入被synchronize修飾的代碼, 所以synchronize也叫重入鎖,這是爲了避免死鎖,已經獲取了這個鎖的線程在再次進入這個代碼塊的時候,如果無法重入,就會被阻塞,造成死鎖。

從執行成本上看,synchronize 是重量級鎖,因爲每次阻塞或者喚醒一條線程,java線程和操作系統線程是一一對應的,所以
操作系統線程也要進行切換,上下文切換的執行的代價比較大。

此外,使用synchronize 無法讓獲取鎖的線程強制釋放鎖,也無法讓被阻塞的線程退出或者超時退出。

java提供了重入鎖ReentrantLock ,提供了等待可中斷,公平鎖,鎖綁定多個條件(synchronize 的wait和notify 只能綁定一個) 三大特性。

2.非阻塞同步
互斥同步是一種悲觀併發策略,其認爲如果不同步,就必定會發生問題。
非阻塞同步是基於衝突檢測的樂觀併發策略。
先進行操作,如果發現衝突,就不斷重試,直到沒有衝突成功爲止,這種策略不需要將線程阻塞,因此是非阻塞同步,需要底層硬件
指令支持。
因爲需要 操作+衝突檢測 這兩個步驟爲原子的。
比較並交換 compar-and-swap CAS

CAS指令需要三個參數 V變量的內存地址, A 舊的值,B新的值 。
原理: 當前僅當V的值爲A時,纔將值更新爲B

缺陷是ABA問題, 也就是舊值在這期間被其他線程更新過,然後又被另一個線程更新回來了,此時當前線程無法判斷A有沒有改變過。
解決方式是加版本號,JUC提供了AtomicStampedReference這個帶標記工具類,但是大多數情況下ABA不影響併發的正確性。

3.無同步方案
1.純代碼 也叫可重入代碼,代碼內部不存在會被多線程併發影響的變量 ,比如說不依賴全局變量,共享資源等
2.線程本地存儲 比如說藉助ThreadLocal。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章