小夥伴們晚上好呀~程序員
乾貨可能會遲到,可是不能缺席呀!😄redis
嘿嘿 這篇來說講鎖 🔒 啦~數據庫
看完上文的 ConcurrentHashMap
是否是發現有不少個 鎖呀,這篇就帶你們縷一縷~ 😝數組
爲啥是 Java 中的鎖呢, 由於 鎖的種類 也有不少的,像咱們平時使用的 MySQL,它也有本身的 表鎖,行鎖,間隙鎖 ... ... 還有 基於redis 的分佈式鎖 (RedLock——紅鎖)呀,zookeeper的分佈式鎖 等各類各樣的🔒~markdown
埋個坑🕳 ~ 之後寫數據庫專題的時候寫寫 😝app
4ye 總結了一份思惟導圖,小夥伴們能夠看看~分佈式
說到這個就不得不提下 JAVA
中的 CAS
了,它是這種思想的具體實現~,還記得上文 頻繁出現的 Unsafe
類嗎,ConcurrentHashMap
就是經過它去調用這個 CAS
( Compare And Swap / Set ),去設置值的 😋學習
讀不加鎖,更新數據期間會加鎖(保證原子性)優化
讀數據時 會很樂觀的認爲別的線程沒有在修改數據,因此不會上鎖。ui
寫數據時 會判斷當前值和指望值一不同,同樣的話會進行修改,此時修改會加鎖。
(這裏還有些很硬核的點,涉及到硬件層面的鎖~ 基於MESI協議滴 後面具體專題再擴展下!😝 )
CAS 機制、版本號機制,時間戳機制
爲何會多後面兩種機制呢,其實這裏是爲了解決這個 ABA 問題
場景模擬,如今有三條線程
線程1 讀取變量a,此時a=1
線程2 讀取變量a,此時a=1, 比較後將它改成 a=2
線程3 讀取變量a,此時a=2, 比較後將它改成 a=1
這時線程1 發現變量a 仍是1 ,和原來同樣,就將它改爲其餘值了
能夠發現這個過程當中 線程1 在修改值的時候,線程2,3已經修改過變量a的值了,可是它絕不知情~
因此呢,爲了解決這個問題,就引入了這個版本號機制 或者 時間戳機制~
其實就是多比較一個值,好比 每次更改時再比較下這個版本號或者時間戳對不對得上~
額 這裏既然只講Java ,那也不扯遠啦~ 嘿嘿,不過道理仍是通用的!
小夥伴們能夠參考下 這個 原子類中的 AtomicStampedReference
,它就解決了這個 ABA 問題
這個就和樂觀徹底相反啦~ 無論讀操做仍是寫操做,都悲觀的認爲會被別的線程改變,因此 不論是讀仍是寫都會 加鎖
悲觀的認爲,讀寫都要加鎖,否則值會被其餘線程改變~
synchronized ,ReentrantLock
公平嘛,要講究先來後到 😄
多個線程按照申請鎖的順序來獲取鎖
原理:主要依賴於維護這個鎖的 等待隊列,當隊列爲空時就直接佔有鎖, 不爲空就加入到 等待隊列 的末尾,而後按照 FIFO 的原則去獲取鎖。
建立 ReentrantLock 時,顯示指定 new ReentrantLock(true)
實際上是靠這個 AQS 來實現公平和非公平的,這裏也埋個坑🕳 後面會詳解這個專題的😋
這個就不和你講先來後到了 😄
多個線程 不按照先到先得的方式去獲取鎖, 有可能後申請的線程會先獲得鎖~
原理:非公平鎖會嘗試獲取鎖,失敗的話會加入到 等待隊列 的末尾,而後按照 FIFO 的原則去獲取鎖 ,變成公平鎖的方式~
建立 ReentrantLock
時,顯示指定 new ReentrantLock(false)
或者使用默認的方式 new ReentrantLock();
還有 synchronized
這個關鍵字也是非公平的
獨自佔有鎖,不和其餘線程共享~ 😄 和 互斥鎖,排他鎖,悲觀鎖 同義
只容許一條線程佔有該鎖
synchronized
,ReentrantLock
還有 ReentrantReadWriteLock
中 的 寫鎖
能夠和其餘線程共享該鎖~ 😄 和 樂觀鎖,讀寫鎖 同義
鎖可被多個線程所持有
ReentrantReadWriteLock
,ReadWriteLock
這兩個中的 讀鎖
能夠理解爲獨佔鎖的具體實現~😄
表示該資源只能被一條線程訪問,不能被其餘訪問
synchronized
,ReentrantLock
顧名思義~ 有讀鎖和寫鎖 😄
讀讀不互斥
讀寫互斥
寫寫互斥
表示該資源容許 多條持有讀鎖的線程共同訪問,可是隻容許一條持有寫鎖的線程獨佔
ReentrantReadWriteLock
,ReadWriteLock
這裏還涉及到鎖的降級,還有可重入等一些有意思的點~ ,埋個坑🕳 後面也會寫到的😋
什麼是可重入呢~ 😄
當一個線程持有某個鎖時,能夠再次獲取該鎖而不會致使死鎖或者阻塞
獲取 n 次 鎖 ,也要釋放 n 次鎖
synchronized
,ReentrantLock
這個主要是 Jdk1.7 版本 的 CurrentHashMap
😄
簡單回憶下~
CurrentHashMap
中 的Segment
數組 ,put 操做時會調用ReentrantLock
的 lock 方法,鎖住該Segment
synchronized
,ReentrantLock
哈哈 看了上文以後是否是以爲這個也特眼熟呀~ 😄
小夥伴們能夠參看下 CurrentHashMap
中源碼對這塊的實現 ,如 put
源碼
讓線程不斷地循環,去嘗試獲取鎖
CAS
這裏其實有不少能夠擴展的,除了它的優缺點以外,還有 自適應自旋 這個和 虛擬機 相關的 ,埋個坑🕳 😋
情景模擬
線程1 擁有 資源A 的鎖,線程2 擁有 資源B 的鎖,可是線程1在持有A鎖的狀況下,還想擁有B鎖。同理 線程2在持有B鎖的狀況下,還想擁有A鎖。他們兩就這樣僵持着,互相等待對方釋放鎖🔒
死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。
無鎖 -> 偏向鎖 -> 輕量級鎖 -> 重量級鎖
這裏涉及到 鎖優化技術 ,後面和 鎖粗化,鎖解除 等做爲一個專題寫寫✍
👉 作了思惟導圖後發現這裏有這麼多專題得縷一縷的 o((>ω< ))o
只能再加把勁啦!
下期見啦各位!😝
做者簡介 :Java4ye 一個在工做日發發技術文,休息日聊聊情感等非技術話題的程序員4ye呀,很高興認識你!!😝
關注公衆號: Java4ye 歡迎關注博主滴我的公衆號~ 這裏給你準備了一系列的學習資源啦,還有各類插件,軟件哦 😋
讓咱們開始這一場意外的相遇吧!~
歡迎留言!謝謝支持!ヾ(≧▽≦*)o