線程安全以及實現方式


線程安全程度
從java語言中各種操作共享數據來分,按照線程安全強度來分:
  • 不可變
  • 絕對線程安全
  • 相對線程安全
  • 線程兼容
  • 線程對立
 
不可變
final帶來的可見性使得一個不可變變量創建出來(沒有使用this引用逃逸出來),永遠不會在多個線程中看到它不一致的狀態。
final修飾的基本數據類型,對於復雜類型,需要定義其內部的變量為final(String,基本類型的對象類型)
 
絕對線程安全
在java api定義的線程安全的類,大多數不是絕對線程安全。
 
 
相對線程安全
相對線程安全就是我們通常說的線程安全,java api大多數的線程安全類都是屬於這種類型。Vector 、HashTable、Collections中的SynchronizedCollection()方法
 
線程兼容
線程兼容指對象本身不是線程安全的,但是可以通過同步手段來保證對象在並發環境下安全的使用。例如 ArrayList、HashMap
 
線程對立
不管是否采用了同步措施,都不能在並發環境下使用的代碼。例如 Thread的suspend()和resume()方法。
 
 
線程安全實現方式 
 
1、互斥同步
Synchronized關鍵字
Synchronized經過編譯之后,會在同步塊前后加上兩條 monitorenter 和 monitorexit 字節碼指令。這兩個字節碼都需要一個reference類型的參數來指明鎖對象,如果指定了一個對象,則是這個對象的reference,如果是實例方法或者靜態方法,則分別是對象的實例(this) 或者 類的class對象
 
根據規范要求,在執行monitorenter指令,首先嘗試去獲取對象的鎖,如果這個對象沒有被鎖定或者當前線程已經獲取到鎖,則鎖的計數器+1,執行monitorexit 指令,鎖計數器-1,鎖釋放了。
規范描述有兩點:
  • Synchronized同步塊對同一個線程是可重入的,不會出現把自己鎖死的情況。
  • 同步塊在一個執行完之前,會阻塞其他線程進入。
 
Synchronized是java語言中的一個重量級的操作,因為java線程是映射到操作系統的原生線程上的,阻塞或者喚醒一條線程,都需要操作系統來幫忙完成,需要從用戶態切換到核心態,轉換需要消耗很多處理時間,可能比用戶代碼執行的時間還長。虛擬機對此作了一些優化,比如 自旋鎖,避免頻繁進入切換到核心態中。
 
ReentrantLock   重入鎖
ReentrantLock  和 Synchronized類似,一個表現為API 層面上的互斥(lock 和 unlock 方法),一個表現為原生語法層面上的互斥。ReentrantLock 比 Synchronized增加了一些高級功能。
  • 等待中斷:持有鎖的線程長期不釋放鎖(執行時間長的同步塊)的時候,正在等待的線程可以選擇放棄等待,做其他事情。
  • 實現公平鎖:ReentrantLock 默認是非公平的,通過構造參數可設置為公平鎖,Synchronized是非公平的 
  • 鎖可以綁定多個條件
 
 
2、非阻塞同步
    互斥同步是一種悲觀的同步策略,非阻塞同步是一種樂觀同步策略,基於一種沖突檢測的策略,就是先進行操作,如果沒有沖突,沒有其他線程爭搶共享數據,那就操作成功,如果存在沖突,則進行其他補救措施(例如常用的 不斷的重試,直到成功為止)
 
CAS操作
 
3、無同步方案
    可重入代碼
    線程本地存儲 ThreadLocal


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM