java併發編程的藝術(五)——五花八門的鎖

多線程編程的很多實現是依靠鎖完成的。

鎖在操作系統層面通過test and set 或者 compare and set等彙編指令實現的

Java通過一些native的compareAndSet方法(每次看到native就蠢蠢欲動要去看native背後又是什麼東西…)

可以說Java所有的鎖以及Java.util.concurrent包都是基於compareAndSet方法的各式組合構建出來的。接下來就是對於Java中鎖的一些功能進行總結。

  1. 輕量與重量級鎖

偏向鎖:偏向於第一個訪問鎖的線程,因爲該線程很有可能下次繼續獲取鎖

輕量級鎖:由偏向鎖升級得到,當有多個線程訪問鎖的時候,偏向鎖將會升級成普通的輕量級鎖

重量級鎖:直接將內存總線鎖住的,比如synchronized關鍵字等

  1. 公平與非公平鎖

公平鎖:線程按照先來後到的順序排隊進行運行。

按照先來後到的順序,每次都只有一個線程執行,其他線程必須等待,效率比較低

非公平鎖:線程通過不斷競爭,誰搶到鎖誰就運行。

可能會造成“飢餓”問題,有些線程一直搶奪不到鎖那就會處於始終運行不到的狀態

  1. 重入與不可重入鎖

可重入鎖:一個線程可以多次獲得的鎖(A獲取鎖之後,在A未被釋放的情況下B也可以獲取鎖)

不可重入鎖:A獲取到鎖後B必須等A釋放鎖才能進一步獲取鎖

  1. 死鎖與活鎖

死鎖:兩個線程互相控制着彼此的資源,A需要獲得B的結果才能繼續進行,而B需要獲得A的結果才能繼續進行

活鎖:兩個線程都不搶奪資源,導致兩個線程始終處於不使用資源的情況。

  1. 悲觀與樂觀鎖

悲觀鎖:對於線程的正確運行持“悲觀”態度,只要有對線程的操作,都進行上鎖操作從而確保線程安全。

線程的安全被確保了,但是效率降低了。

樂觀鎖:

對線程的運行保持“樂觀”態度,即默認線程不會對數據進行修改操作,而是通過CAS或者版本號等方式去判斷之前有沒有人修改數據,若發現了修改且修改有效後再更新拿到的舊值。

  1. 讀寫鎖

定義:把對共享資源的訪問劃分爲讀與寫,多讀者不存在矛盾,而寫鎖必須與其他互斥,即只能有一個線程去進行寫。

共享鎖:又可以被稱作“讀鎖”,只支持對數據的讀取操作但不支持修改操作。

排他鎖:又被稱爲“寫鎖”,支持對於數據的讀取和修改操作,擁有排他鎖的線程不允許其他線程讀取和修改。

  1. 其他

自旋鎖:如果一個線程佔用鎖的時間比較短,那麼其它的線程不需要內核態和用戶態切換進入阻塞狀態,而是通過無限循環反覆檢查鎖變量是否可用(所謂的稍微等一等),直到獲取到鎖。

互斥鎖:一次最多只能有一個線程持有這把鎖。

分段鎖:concurrentHashMap中使用到segment,通過把要存放的數據分成一段一段,每一段都給一個鎖,確保該段數據的線程安全。

閉鎖:countDownLatch, 可以用來確保一個線程等到其他線程完成後開始執行。就像一扇門,等所有的人都到齊了再打開讓所有人一起進去。

信號量:n條線程訪問m個資源(m<n)統一時刻只允許m條線程訪問資源。

同步屏障:線程阻塞在屏障處,等所有線程都到了屏障的位置之後,屏障再打開讓所有線程都開始運行。

喜歡的話轉發分享,點亮“在看”噢~
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章