悲觀鎖和樂觀鎖是面試的高頻問題git
咱們應該有一些概念github
悲觀鎖顧名思義,就是悲觀的認爲只要不作正確的同步措施,他就必定會出現問題面試
樂觀鎖是說對於數據的同步我樂觀的認爲不採用同步措施也不會產生問題,可是若是產生了問題我就進行補救措施,好比retry算法
多線程間的同步機制主要有四種spring
先不講這四個的區別,只要先記住,Synchronized就是使用操做系統的互斥量安全
個人全部文章同步更新與Github--Java-Notes,想了解JVM(基本更完),HashMap源碼分析,spring相關,,併發,劍指offer題解(Java版),能夠點個star。能夠看個人github主頁,天天都在更新喲。多線程
邀請您跟我一同完成 repo併發
互斥只是同步機制的其中一個手段,也是很常見的保障併發正確性的手段函數
咱們知道傳統的鎖(如synchronized或者reentrantLock)之因此被稱爲重量級鎖,就是由於他使用操做系統互斥量來實現同步源碼分析
這是咱們相對來講最熟悉的方式,通常新手學同步的時候,咱們都是採用這個關鍵字,可是這個方式由於是使用操做系統信號量,因此相對來講效率比較低
Java中 synchronized能實現同步基礎:Java中的對象均可以做爲鎖
JVM 是基於 進入和退出 monitor對象來實現方法同步和代碼塊同步
當synchronized關鍵字通過編譯後,會在同步塊的先後(同步代碼塊開始和結束或者異常的地方)分別造成 monitorenter
和 monitorexit
兩個字節碼指令。
同步方法規範中並無明說,可是同步方法也但是使用上面兩個指令來實現
在基本用法上,ReentrantLock和Synchronized很類似
相比Synchronized,ReentrantLock增長了以下功能:
在最新的版本中,對於Synchronized 的優化很是大,性能已經和ReentrantLock差不太多,因此若是不是使用那些高級功能,仍是建議使用 synchronized。畢竟後面仍是會大力優化Synchronized
下面是JDK8下,兩者性能對比(前者是自增,後者是鏈表)
樂觀鎖不須要線程掛起等待,因此也叫非阻塞同步
通常在一個數據表中加一個 version字段,表示這個數據被更新的次數,當這個數據被修改一次,版本號就加一。
提交版本必須大於當前記錄的版本
舉個例子
我如今銀行帳戶有 10元,如今有一個version字段,版本號爲 1.
如今我A操做取出2元,我先讀入數據 version =1,而後扣除
與此同時,B操做也要取出1元,讀入數據 version =1,而後扣除
這個時候,A操做完成,上傳數據,版本號加一,version=2,這個版本大於當前的記錄值 1,因此更新操做完成
這個時候,B操做也完成了,也要更新,他的版本號加一,version=2,而後更新的時候發現這個版本號和當前記錄的版本號相同,不知足提交版本號必須大於當前記錄的版本號的條件,不容許更新
這個時候,B操做就要從新讀入再重複以前的步驟
經過這樣的方法,咱們就保證了B操做不會將A操做所更新的值覆蓋,保證了數據的同步
CAS算法是基於硬件的發展產生的。爲何呢,由於咱們須要這兩個原子步驟
可是咱們如何保證上面的兩個步驟是安全的?確定不能使用互斥同步,若是使用了也不是非阻塞式了。這個時候咱們就要依賴硬件指令了,讓看似不少步驟的命令,可以使用一條指令就能完成,好比:
前面的三條是很早以前就有的指令,後面的兩條是現代的處理器纔有的指令
CAS有三個數。
當且僅當V的值符合A的值的時候,處理器纔會使用B值來更新V中的值;不然他就不執行更新。(上述比較和更新是一個原子操做)。通常狀況下這個操做是自旋的,也就是會不斷嘗試,直到完成
變成1A -> 2B -> 3A
AtomicStampedReference
,而後可使用compareAndSet
方法。他首先檢查當前引用是否爲預期引用,而後檢查當前標誌是否等於預期標誌
pause
指令,性能會有必定提高
AtomicReference
類,能夠把多個變量放到一個對象中,從而使用CAS操做悲觀鎖適合寫,樂觀鎖適合讀