求求你不要再問我重量級鎖、自旋鎖、輕量級鎖、偏向鎖、悲觀、樂觀鎖等各類鎖 ---- 不看後悔系列

重量級鎖?自旋鎖?自適應自旋鎖?輕量級鎖?偏向鎖?悲觀鎖?樂觀鎖?執行一個方法咋這麼辛苦,處處都是鎖。面試

今天這篇文章,給你們普及下這些鎖到底是啥,他們的由來,他們之間有啥關係,有啥區別。算法

重量級鎖

若是你學過多線程,那麼你確定知道這個東西,至於爲何須要鎖,我就不給你普及了,就當作你是已經懂的了。安全

咱們知道,咱們要進入一個同步、線程安全的方法時,是須要先得到這個方法的鎖的,退出這個方法時,則會釋放鎖。若是獲取不到這個鎖的話,意味着有別的線程在執行這個方法,這時咱們就會立刻進入阻塞的狀態,等待那個持有鎖的線程釋放鎖,而後再把咱們從阻塞的狀態喚醒,咱們再去獲取這個方法的鎖。多線程

這種獲取不到鎖就立刻進入阻塞狀態的鎖,咱們稱之爲重量級鎖併發

自旋鎖

咱們知道,線程從運行態進入阻塞態這個過程,是很是耗時的,由於不只須要保存線程此時的執行狀態,上下文等數據,還涉及到用戶態內核態的轉換。固然,把線程從阻塞態喚醒也是同樣,也是很是消耗時間的。工具

剛纔我說線程拿不到鎖,就會立刻進入阻塞狀態,然而現實是,它雖然這一刻拿不到鎖,可能在下 0.0001 秒,就有其餘線程把這個鎖釋放了。若是它慢0.0001秒來拿這個鎖的話,可能就能夠順利拿到了,不須要經歷阻塞/喚醒這個花時間的過程了。佈局

然而重量級鎖就是這麼坑,它就是不願等待一下,一拿不到就是要立刻進入阻塞狀態。爲了解決這個問題,咱們引入了另一種願意等待一段時間的鎖 --- 自旋鎖優化

自旋鎖就是,若是此時拿不到鎖,它不立刻進入阻塞狀態,而是等待一段時間,看看這段時間有沒其餘人把這鎖給釋放了。怎麼等呢?這個就相似於線程在那裏作空循環,若是循環必定的次數還拿不到鎖,那麼它纔會進入阻塞的狀態。操作系統

至因而循環等待幾回,這個是能夠人爲指定一個數字的。.net

自適應自旋鎖

上面咱們說的自旋鎖,每一個線程循環等待的次數都是同樣的,例如我設置爲 100次的話,那麼線程在空循環 100 次以後還沒拿到鎖,就會進入阻塞狀態了。

而自適應自旋鎖就牛逼了,它不須要咱們人爲指定循環幾回,它本身自己會進行判斷要循環幾回,並且每一個線程可能循環的次數也是不同的。而之因此這樣作,主要是咱們以爲,若是一個線程在不久前拿到過這個鎖,或者它以前常常拿到過這個鎖,那麼咱們認爲它再次拿到鎖的概率很是大,因此循環的次數會多一些。

而若是有些線程歷來就沒有拿到過這個鎖,或者說,平時不多拿到,那麼咱們認爲,它再次拿到的機率是比較小的,因此咱們就讓它循環的次數少一些。由於你在那裏作空循環是很消耗 CPU 的。

因此這種可以根據線程最近得到鎖的狀態來調整循環次數的自旋鎖,咱們稱之爲自適應自旋鎖

輕量級鎖

上面咱們介紹的三種鎖:重量級、自旋鎖和自適應自旋鎖,他們都有一個特色,就是進入一個方法的時候,就會加上鎖,退出一個方法的時候,也就釋放對應的鎖。

之因此要加鎖,是由於他們懼怕本身在這個方法執行的時候,被別人偷偷進來了,因此只能加鎖,防止其餘線程進來。這就至關於,每次離開本身的房間,都要鎖上門,人回來了再把鎖解開。

這實在是太麻煩了,若是根本就沒有線程來和他們競爭鎖,那他們不是白白上鎖了?要知道,加鎖這個過程是須要操做系統這個大佬來幫忙的,是很消耗時間的,。爲了解決這種動不動就加鎖帶來的開銷,輕量級鎖出現了。

輕量級鎖認爲,當你在方法裏面執行的時候,實際上是不多恰好有人也來執行這個方法的,因此,當咱們進入一個方法的時候根本就不用加鎖,咱們只須要作一個標記就能夠了,也就是說,咱們能夠用一個變量來記錄此時該方法是否有人在執行。也就是說,若是這個方法沒人在執行,當咱們進入這個方法的時候,採用CAS機制,把這個方法的狀態標記爲已經有人在執行,退出這個方法時,在把這個狀態改成了沒有人在執行了。

之因此要用CAS機制來改變狀態,是由於咱們對這個狀態的改變,不是一個原子性操做,因此須要CAS機制來保證操做的原子性。不知道CAS的能夠看這篇文章:併發的核心:CAS 是什麼?Java8是如何優化 CAS 的?

顯然,比起加鎖操做,這個採用CAS來改變狀態的操做,花銷就小多了。

然而可能會說,沒人來競爭的這種想法,那是你說的而已,那若是萬一有人來競爭說呢?也就是說,當一個線程來執行一個方法的時候,方法裏面已經有人在執行了。

若是真的遇到了競爭,咱們就會認爲輕量級鎖已經不適合了,咱們就會把輕量級鎖升級爲重量級鎖了。

因此輕量級鎖適合用在那種,不多出現多個線程競爭一個鎖的狀況,也就是說,適合那種多個線程老是錯開時間來獲取鎖的狀況。

偏向鎖

偏向鎖就更加牛逼了,咱們已經以爲輕量級鎖已經夠,然而偏向鎖更加省事,偏向鎖認爲,你輕量級鎖每次進入一個方法都須要用CAS來改變狀態,退出也須要改變,多麻煩。

偏向鎖認爲,其實對於一個方法,是不多有兩個線程來執行的,搞來搞去,其實也就一個線程在執行這個方法而已,至關於單線程的狀況,竟然是單線程,那就不必加鎖了。

不過畢竟實際狀況的多線程,單線程只是本身認爲的而已了,因此呢,偏向鎖進入一個方法的時候是這樣處理的:若是這個方法沒有人進來過,那麼一個線程首次進入這個方法的時候,會採用CAS機制,把這個方法標記爲有人在執行了,和輕量級鎖加鎖有點相似,而且也會把該線程的 ID 也記錄進去,至關於記錄了哪一個線程在執行。

而後,但這個線程退出這個方法的時候,它不會改變這個方法的狀態,而是直接退出來,懶的去改,由於它認爲除了本身這個線程以外,其餘線程並不會來執行這個方法。

而後當這個線程想要再次進入這個方法的時候,會判斷一下這個方法的狀態,若是這個方法已經被標記爲有人在執行了,而且線程的ID是本身,那麼它就直接進入這個方法執行,啥也不用作

你看,多方便,第一次進入須要CAS機制來設置,之後進出就啥也不用幹了,直接進入退出。

然而,現實老是殘酷的,畢竟實際狀況仍是多線程,因此萬一有其餘線程來進入這個方法呢?若是真的出現這種狀況,其餘線程一看這個方法的ID不是本身,這個時候說明,至少有兩個線程要來執行這個方法論,這意味着偏向鎖已經不適用了,這個時候就會從偏向鎖升級爲輕量級鎖。

因此呢,偏向鎖適用於那種,始終只有一個線程在執行一個方法的狀況哦。

這裏我做下說明,爲了方便你們理解,我在將輕量級鎖和偏向鎖的時候,實際上是簡化了不少的,否則的話會涉及到對象的內部結構、佈局,我以爲把那些扯出來,大家可能要暈了,因此我大體講了他們的原理。

悲觀鎖和樂觀鎖

最開始咱們說的三種鎖,重量級鎖、自旋鎖和自適應自旋鎖,進入方法以前,就必定要先加一個鎖,這種咱們爲稱之爲悲觀鎖。悲觀鎖總認爲,若是不事先加鎖的話,就會出事,這種想法確實悲觀了點,這估計就是悲觀鎖的來源了。

而樂觀鎖卻相反,認爲不加鎖也沒事,咱們能夠先不加鎖,若是出現了衝突,咱們在想辦法解決,例如 CAS 機制,上面說的輕量級鎖,就是樂觀鎖的。不會立刻加鎖,而是等待真的出現了衝突,在想辦法解決。不知道 CAS 機制的,能夠看我以前寫的這篇文章哦:併發的核心:CAS 是什麼?Java8是如何優化 CAS 的?

總結

到這裏也大體寫完了,簡單介紹普及了一下,重點的你們要理解他們的由來,原理。每一種鎖都有他們的應用以及各自的優缺點,若是有機會,我再給你們說說他們各自的應用場景,優缺點,這個面試的時候,好像也會被常常到,今天先寫到這裏勒。

你們能夠說說這些鎖的優缺點哦,例如與重量級鎖相比,自旋鎖容量致使什麼問題的發生?悲觀鎖和樂觀鎖的比較呢?你們也能夠評論區說說勒,這些是必定要搞懂的哦。

最後推薦下個人公衆號:苦逼的碼農,主要分享一下技術文章、面試題、算法題,各類工具、視頻資源等,裏面已有100多篇原創文章,期待各路英雄來交流,點擊便可掃碼關注戳我便可關注

相關文章
相關標籤/搜索