5000字 | 24張圖帶你完全理解Java中的21種鎖

本篇主要內容以下:java

幫你總結好的鎖:面試

序號 鎖名稱 應用
1 樂觀鎖 CAS
2 悲觀鎖 synchronized、vector、hashtable
3 自旋鎖 CAS
4 可重入鎖 synchronized、Reentrantlock、Lock
5 讀寫鎖 ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet
6 公平鎖 Reentrantlock(true)
7 非公平鎖 synchronized、reentrantlock(false)
8 共享鎖 ReentrantReadWriteLock中讀鎖
9 獨佔鎖 synchronized、vector、hashtable、ReentrantReadWriteLock中寫鎖
10 重量級鎖 synchronized
11 輕量級鎖 鎖優化技術
12 偏向鎖 鎖優化技術
13 分段鎖 concurrentHashMap
14 互斥鎖 synchronized
15 同步鎖 synchronized
16 死鎖 相互請求對方的資源
17 鎖粗化 鎖優化技術
18 鎖消除 鎖優化技術

一、樂觀鎖

樂觀鎖編程

樂觀鎖是一種樂觀思想,假定當前環境是讀多寫少,遇到併發寫的機率比較低,讀數據時認爲別的線程不會正在進行修改(因此沒有上鎖)。寫數據時,判斷當前 與指望值是否相同,若是相同則進行更新(更新期間加鎖,保證是原子性的)。小程序

Java中的樂觀鎖 CAS,比較並替換,比較當前值(主內存中的值),與預期值(當前線程中的值,主內存中值的一份拷貝)是否同樣,同樣則更新,不然繼續進行CAS操做。數組

如上圖所示,能夠同時進行讀操做,讀的時候其餘線程不能進行寫操做。安全

二、悲觀鎖

悲觀鎖服務器

悲觀鎖是一種悲觀思想,即認爲寫多讀少,遇到併發寫的可能性高,每次去拿數據的時候都認爲其餘線程會修改,因此每次讀寫數據都會認爲其餘線程會修改,因此每次讀寫數據時都會上鎖。其餘線程想要讀寫這個數據時,會被這個線程block,直到這個線程釋放鎖而後其餘線程獲取到鎖。微信

Java中的悲觀鎖 synchronized修飾的方法和方法塊、ReentrantLock多線程

如上圖所示,只能有一個線程進行讀操做或者寫操做,其餘線程的讀寫操做均不能進行。架構

三、自旋鎖

mark

自旋鎖是一種技術: 爲了讓線程等待,咱們只須讓線程執行一個忙循環(自旋)。

如今絕大多數的我的電腦和服務器都是多路(核)處理器系統,若是物理機器有一個以上的處理器或者處理器核心,能讓兩個或以上的線程同時並行執行,就可讓後面請求鎖的那個線程「稍等一會」,但不放棄處理器的執行時間,看看持有鎖的線程是否很快就會釋放鎖。

自旋鎖的優勢: 避免了線程切換的開銷。掛起線程和恢復線程的操做都須要轉入內核態中完成,這些操做給Java虛擬機的併發性能帶來了很大的壓力。

自旋鎖的缺點: 佔用處理器的時間,若是佔用的時間很長,會白白消耗處理器資源,而不會作任何有價值的工做,帶來性能的浪費。所以自旋等待的時間必須有必定的限度,若是自旋超過了限定的次數仍然沒有成功得到鎖,就應當使用傳統的方式去掛起線程。

自旋次數默認值:10次,可使用參數-XX:PreBlockSpin來自行更改。

自適應自旋 自適應意味着自旋的時間再也不是固定的,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定的。有了自適應自旋,隨着程序運行時間的增加及性能監控信息的不斷完善,虛擬機對程序鎖的狀態預測就會愈來愈精準。

Java中的自旋鎖 CAS操做中的比較操做失敗後的自旋等待。

四、可重入鎖(遞歸鎖)

可重入鎖

可重入鎖是一種技術: 任意線程在獲取到鎖以後可以再次獲取該鎖而不會被鎖所阻塞。

可重入鎖的原理: 經過組合自定義同步器來實現鎖的獲取與釋放。

  • 再次獲取鎖:識別獲取鎖的線程是否爲當前佔據鎖的線程,若是是,則再次成功獲取。獲取鎖後,進行計數自增,

  • 釋放鎖:釋放鎖時,進行計數自減。

Java中的可重入鎖 ReentrantLock、synchronized修飾的方法或代碼段。

可重入鎖的做用: 避免死鎖。

面試題1: 可重入鎖若是加了兩把,可是隻釋放了一把會出現什麼問題?

答:程序卡死,線程不能出來,也就是說咱們申請了幾把鎖,就須要釋放幾把鎖。

面試題2: 若是隻加了一把鎖,釋放兩次會出現什麼問題?

答:會報錯,java.lang.IllegalMonitorStateException。

五、讀寫鎖

讀寫鎖是一種技術: 經過ReentrantReadWriteLock類來實現。爲了提升性能, Java 提供了讀寫鎖,在讀的地方使用讀鎖,在寫的地方使用寫鎖,靈活控制,若是沒有寫鎖的狀況下,讀是無阻塞的,在必定程度上提升了程序的執行效率。讀寫鎖分爲讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由 jvm 本身控制的。

讀鎖: 容許多個線程獲取讀鎖,同時訪問同一個資源。

讀鎖

寫鎖: 只容許一個線程獲取寫鎖,不容許同時訪問同一個資源。

寫鎖

如何使用:

/** * 建立一個讀寫鎖 * 它是一個讀寫融爲一體的鎖,在使用的時候,須要轉換 */
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

獲取讀鎖和釋放讀鎖

// 獲取讀鎖
rwLock.readLock().lock();

// 釋放讀鎖
rwLock.readLock().unlock();

獲取寫鎖和釋放寫鎖

// 建立一個寫鎖
rwLock.writeLock().lock();

// 寫鎖 釋放
rwLock.writeLock().unlock();

Java中的讀寫鎖:ReentrantReadWriteLock

六、公平鎖

公平鎖

公平鎖是一種思想: 多個線程按照申請鎖的順序來獲取鎖。在併發環境中,每一個線程會先查看此鎖維護的等待隊列,若是當前等待隊列爲空,則佔有鎖,若是等待隊列不爲空,則加入到等待隊列的末尾,按照FIFO的原則從隊列中拿到線程,而後佔有鎖。

七、非公平鎖

非公平鎖

非公平鎖是一種思想: 線程嘗試獲取鎖,若是獲取不到,則再採用公平鎖的方式。多個線程獲取鎖的順序,不是按照先到先得的順序,有可能後申請鎖的線程比先申請的線程優先獲取鎖。

優勢: 非公平鎖的性能高於公平鎖。

缺點: 有可能形成線程飢餓(某個線程很長一段時間獲取不到鎖)

Java中的非公平鎖:synchronized是非公平鎖,ReentrantLock經過構造函數指定該鎖是公平的仍是非公平的,默認是非公平的。

八、共享鎖

共享鎖

共享鎖是一種思想: 能夠有多個線程獲取讀鎖,以共享的方式持有鎖。和樂觀鎖、讀寫鎖同義。

Java中用到的共享鎖:  ReentrantReadWriteLock

九、獨佔鎖

獨佔鎖

獨佔鎖是一種思想: 只能有一個線程獲取鎖,以獨佔的方式持有鎖。和悲觀鎖、互斥鎖同義。

Java中用到的獨佔鎖: synchronized,ReentrantLock

十、重量級鎖

重量級鎖

重量級鎖是一種稱謂: synchronized是經過對象內部的一個叫作監視器鎖(monitor)來實現的,監視器鎖自己依賴底層的操做系統的 Mutex Lock來實現。操做系統實現線程的切換須要從用戶態切換到核心態,成本很是高。這種依賴於操做系統 Mutex Lock來實現的鎖稱爲重量級鎖。爲了優化synchonized,引入了輕量級鎖偏向鎖

Java中的重量級鎖: synchronized

十一、輕量級鎖

輕量級鎖

輕量級鎖是JDK6時加入的一種鎖優化機制: 輕量級鎖是在無競爭的狀況下使用CAS操做去消除同步使用的互斥量。輕量級是相對於使用操做系統互斥量來實現的重量級鎖而言的。輕量級鎖在沒有多線程競爭的前提下,減小傳統的重量級鎖使用操做系統互斥量產生的性能消耗。若是出現兩條以上的線程爭用同一個鎖的狀況,那輕量級鎖將不會有效,必須膨脹爲重量級鎖。

優勢: 若是沒有競爭,經過CAS操做成功避免了使用互斥量的開銷。

缺點: 若是存在競爭,除了互斥量自己的開銷外,還額外產生了CAS操做的開銷,所以在有競爭的狀況下,輕量級鎖比傳統的重量級鎖更慢。

十二、偏向鎖

偏向鎖

偏向鎖是JDK6時加入的一種鎖優化機制: 在無競爭的狀況下把整個同步都消除掉,連CAS操做都不去作了。偏是指偏愛,它的意思是這個鎖會偏向於第一個得到它的線程,若是在接下來的執行過程當中,該鎖一直沒有被其餘的線程獲取,則持有偏向鎖的線程將永遠不須要再進行同步。持有偏向鎖的線程之後每次進入這個鎖相關的同步塊時,虛擬機均可以再也不進行任何同步操做(例如加鎖、解鎖及對Mark Word的更新操做等)。

優勢: 把整個同步都消除掉,連CAS操做都不去作了,優於輕量級鎖。

缺點: 若是程序中大多數的鎖都老是被多個不一樣的線程訪問,那偏向鎖就是多餘的。

1三、分段鎖

分段鎖

分段鎖是一種機制: 最好的例子來講明分段鎖是ConcurrentHashMap。ConcurrentHashMap原理:它內部細分了若干個小的 HashMap,稱之爲段(Segment)。默認狀況下一個 ConcurrentHashMap 被進一步細分爲 16 個段,既就是鎖的併發度。若是須要在 ConcurrentHashMap 添加一項key-value,並非將整個 HashMap 加鎖,而是首先根據 hashcode 獲得該key-value應該存放在哪一個段中,而後對該段加鎖,並完成 put 操做。在多線程環境中,若是多個線程同時進行put操做,只要被加入的key-value不存放在同一個段中,則線程間能夠作到真正的並行。

線程安全:ConcurrentHashMap 是一個 Segment 數組, Segment 經過繼承ReentrantLock 來進行加鎖,因此每次須要加鎖的操做鎖住的是一個 segment,這樣只要保證每一個 Segment 是線程安全的,也就實現了全局的線程安全

1四、互斥鎖

互斥鎖

互斥鎖與悲觀鎖、獨佔鎖同義,表示某個資源只能被一個線程訪問,其餘線程不能訪問。

  • 讀-讀互斥

  • 讀-寫互斥

  • 寫-讀互斥

  • 寫-寫互斥

Java中的同步鎖: synchronized

1五、同步鎖

同步鎖

同步鎖與互斥鎖同義,表示併發執行的多個線程,在同一時間內只容許一個線程訪問共享數據。

Java中的同步鎖: synchronized

1六、死鎖

死鎖

死鎖是一種現象:如線程A持有資源x,線程B持有資源y,線程A等待線程B釋放資源y,線程B等待線程A釋放資源x,兩個線程都不釋放本身持有的資源,則兩個線程都獲取不到對方的資源,就會形成死鎖。

Java中的死鎖不能自行打破,因此線程死鎖後,線程不能進行響應。因此必定要注意程序的併發場景,避免形成死鎖。

1七、鎖粗化

鎖粗化

鎖粗化是一種優化技術: 若是一系列的連續操做都對同一個對象反覆加鎖和解鎖,甚至加鎖操做都是出如今循環體體之中,就算真的沒有線程競爭,頻繁地進行互斥同步操做將會致使沒必要要的性能損耗,因此就採起了一種方案:把加鎖的範圍擴展(粗化)到整個操做序列的外部,這樣加鎖解鎖的頻率就會大大下降,從而減小了性能損耗。

1八、鎖消除

鎖消除

鎖消除是一種優化技術: 就是把鎖幹掉。當Java虛擬機運行時發現有些共享數據不會被線程競爭時就能夠進行鎖消除。

那如何判斷共享數據不會被線程競爭?

利用逃逸分析技術:分析對象的做用域,若是對象在A方法中定義後,被做爲參數傳遞到B方法中,則稱爲方法逃逸;若是被其餘線程訪問,則稱爲線程逃逸。

在堆上的某個數據不會逃逸出去被其餘線程訪問到,就能夠把它看成棧上數據對待,認爲它是線程私有的,同步加鎖就不須要了。

1九、synchronized

synchronized

synchronized是Java中的關鍵字:用來修飾方法、對象實例。屬於獨佔鎖、悲觀鎖、可重入鎖、非公平鎖。

  • 1.做用於實例方法時,鎖住的是對象的實例(this);

  • 2.看成用於靜態方法時,鎖住的是 Class類,至關於類的一個全局鎖, 會鎖全部調用該方法的線程;

  • 3.synchronized 做用於一個非 NULL的對象實例時,鎖住的是全部以該對象爲鎖的代碼塊。它有多個隊列,當多個線程一塊兒訪問某個對象監視器的時候,對象監視器會將這些線程存儲在不一樣的容器中。

每一個對象都有個 monitor 對象, 加鎖就是在競爭 monitor 對象,代碼塊加鎖是在代碼塊先後分別加上 monitorenter 和 monitorexit 指令來實現的,方法加鎖是經過一個標記位來判斷的。

20、Lock和synchronized的區別

自動擋和手動擋的區別

Lock 是Java中的接口,可重入鎖、悲觀鎖、獨佔鎖、互斥鎖、同步鎖。

  • 1.Lock須要手動獲取鎖和釋放鎖。就比如自動擋和手動擋的區別

  • 2.Lock 是一個接口,而 synchronized 是 Java 中的關鍵字, synchronized 是內置的語言實現。

  • 3.synchronized 在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而 Lock 在發生異常時,若是沒有主動經過 unLock()去釋放鎖,則極可能形成死鎖現象,所以使用 Lock 時須要在 finally 塊中釋放鎖。

  • 4.Lock 可讓等待鎖的線程響應中斷,而 synchronized 卻不行,使用 synchronized 時,等待的線程會一直等待下去,不可以響應中斷。

  • 5.經過 Lock 能夠知道有沒有成功獲取鎖,而 synchronized 卻沒法辦到。

  • 6.Lock 能夠經過實現讀寫鎖提升多個線程進行讀操做的效率。

synchronized的優點:

  • 足夠清晰簡單,只須要基礎的同步功能時,用synchronized。

  • Lock應該確保在finally塊中釋放鎖。若是使用synchronized,JVM確保即便出現異常,鎖也能被自動釋放。

  • 使用Lock時,Java虛擬機很可貴知哪些鎖對象是由特定線程鎖持有的。

2一、ReentrantLock 和synchronized的區別

Lock、ReentrantLock、shnchronzied

ReentrantLock是Java中的類 : 繼承了Lock類,可重入鎖、悲觀鎖、獨佔鎖、互斥鎖、同步鎖。

劃重點

相同點:

  • 1.主要解決共享變量如何安全訪問的問題

  • 2.都是可重入鎖,也叫作遞歸鎖,同一線程能夠屢次得到同一個鎖,

  • 3.保證了線程安全的兩大特性:可見性、原子性。

不一樣點:

  • 1.ReentrantLock 就像手動汽車,須要顯示的調用lock和unlock方法, synchronized 隱式得到釋放鎖。

  • 2.ReentrantLock 可響應中斷, synchronized 是不能夠響應中斷的,ReentrantLock 爲處理鎖的不可用性提供了更高的靈活性

  • 3.ReentrantLock 是 API 級別的, synchronized 是 JVM 級別的

  • 4.ReentrantLock 能夠實現公平鎖、非公平鎖,默認非公平鎖,synchronized 是非公平鎖,且不可更改。

  • 5.ReentrantLock 經過 Condition 能夠綁定多個條件

彩蛋: 講了那麼多鎖,都跟阻塞相關,寶寶想聽阻塞呀!

我是悟空,一隻努力變強的碼農!我要變身超級賽亞人啦!

你好,我是悟空哥7年項目開發經驗,全棧工程師,開發組長,超喜歡圖解編程底層原理。正在編寫兩本PDF,分別是 一、Spring Cloud實戰項目(佳必過),二、Java併發必知必會。我還手寫了2個小程序,Java刷題小程序,PMP刷題小程序,點擊個人公衆號菜單打開!另外有111本架構師資料以及1000道Java面試題,都整理成了PDF,能夠關注公衆號 悟空聊架構 回覆 悟空 領取優質資料。

轉發->在看->點贊->收藏->評論!!!是對我最大的支持!

- END -

本文分享自微信公衆號 - 悟空聊架構(PassJava666)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索