【synchronized底層原理之2】悲觀鎖與樂觀鎖、線程阻塞的代價等

悲觀鎖與樂觀鎖

悲觀鎖(Pessimistic Lock)

悲觀鎖是就是悲觀思想,即認爲寫多,遇到併發寫的可能性高,每次去拿數據的時候都認爲別人會修改,因此每次在讀寫數據的時候都會上鎖,這樣別人想讀寫這個數據就會block直到拿到鎖。因爲數據進行加鎖,期間對該數據進行讀寫的其餘線程都會進行等待。java

synchronized的重量級鎖就是悲觀鎖。AQS框架下的鎖則是先嚐試cas樂觀鎖去獲取鎖,獲取不到,纔會轉換爲悲觀鎖,如RetreenLock。數據結構

樂觀鎖(Optimistic Lock)

樂觀鎖是一種樂觀思想,即認爲讀多寫少,遇到併發寫的可能性低,每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,採起在寫時先讀出當前版本號,而後加鎖操做(比較跟上一次的版本號,若是同樣則更新),若是失敗則要重複讀-比較-寫的操做。因爲數據沒有進行加鎖,期間該數據能夠被其餘線程進行讀寫操做。併發

java中的樂觀鎖基本都是經過CAS操做實現的,CAS是一種更新的原子操做,比較當前值跟傳入值是否同樣,同樣則更新,不然失敗。框架

synchronized的偏向鎖和輕量級鎖就是樂觀鎖spa

★小結

在Java技術發展史上,最開始使用的是悲觀鎖,實踐中發現使用悲觀鎖有不少缺點,因此又引入了樂觀鎖。操作系統

兩種鎖各有優缺點,讀取頻繁使用樂觀鎖(可是可能出現「髒」讀),寫入頻繁使用悲觀鎖(上下文切換開銷大,可是保證沒有「髒」數據)。線程

synchronized的重量級鎖就是悲觀鎖指針

synchronized的偏向鎖和輕量級鎖就是樂觀鎖對象

java線程阻塞的代價--主要是上下文切換

Java線程的上下文切換須要操做系統的介入

java的線程是映射到操做系統原生線程之上的,若是要阻塞或喚醒一個線程就須要操做系統介入。blog

操做系統切換線程須要在用戶態與內核態之間切換:由於用戶態和內核態內存空間各自獨立,故而切換要傳遞變量和參數,這樣的話系統消耗較大,費時

須要在用戶態與內核態之間切換,這種切換會消耗大量的系統資源,由於用戶態與內核態都有各自專用的內存空間,專用的寄存器等,用戶態切換至內核態須要傳遞給許多變量、參數給內核,內核也須要保護好用戶態在切換時的一些寄存器值、變量等,以便內核態調用結束後切換回用戶態繼續工做。

頻繁的上下文切換很費時,若是同步代碼執行所需時間比上下文切換時間都要短,那引入重量級鎖切換上下文這種同步策略是失敗的:因此synchronized從JKD1.6進行了改進,引入了偏向鎖、輕量級鎖

java對象頭中的markword

markword是java對象數據結構中的一部分,對象的markword和java各類類型的鎖密切相關。

markword數據的長度在32位和64位的虛擬機(未開啓壓縮指針)中分別爲32bit和64bit,它的最後2bit是鎖狀態標誌位,用來標記當前對象的狀態,對象的所處的狀態,決定了markword存儲的內容,以下表所示

32位虛擬機在不一樣狀態下markword結構以下圖所示

瞭解了markword結構,有助於瞭解java鎖的加鎖解鎖過程。

相關文章
相關標籤/搜索