亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java多線程中常見的鎖策略詳解

 更新時間:2023年07月20日 11:23:32   作者:一只愛打拳的程序猿  
這篇文章主要介紹了Java多線程中常見的鎖策略詳解,在Java多線程中鎖(synchronized)也會根據(jù)鎖的競爭程度來升級為相關(guān)“高等級”鎖,本文為了更好的理解?synchronized?加鎖機(jī)制,對其做出了詳細(xì)解釋,需要的朋友可以參考下

1. 悲觀鎖與樂觀鎖

悲觀鎖:為了保證原子性,因此把數(shù)據(jù)進(jìn)行上鎖,每一個不同的線程拿數(shù)據(jù)的時候都會參與鎖的競爭,其他線程想必須等待前者拿完數(shù)據(jù)解鎖后才能參與拿數(shù)據(jù)。

舉例,由于維修導(dǎo)致一層樓只剩下一間廁所。因此,線程1進(jìn)入廁所后,其他線程只能阻塞等待。

樂觀鎖:假設(shè)數(shù)據(jù)一般情況下不會產(chǎn)生并發(fā)沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時候,才會正式對數(shù)據(jù)是否產(chǎn)生并發(fā)沖突進(jìn)行檢測,如果發(fā)現(xiàn)并發(fā)沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。

還是上述上廁所例子,線程1 給其他線返回一個信息,其他線程可根據(jù)信息選擇換樓層上廁所亦或是等待。

以上的上廁所例子,在樓棟廁所不充足情況下。線程還是盲目選擇這棟樓的廁所(使用悲觀鎖)就會導(dǎo)致阻塞消耗系統(tǒng)資源。在樓棟廁所充足的情況下,線程選擇了這棟樓的廁所(使用樂觀鎖)這樣就能很好的利用系統(tǒng)資源。

synchronized 初始情況下使用的是樂觀鎖,當(dāng)發(fā)現(xiàn)鎖競爭激烈時候就會自動轉(zhuǎn)換為悲觀鎖。就好比一個線程去某一棟樓上廁所,并不知道該樓棟是否廁所充足。充足就不阻塞等待,不充足就阻塞等待。

2. 讀寫鎖與互斥鎖

多線程中,線程作為讀取方不會產(chǎn)生線程安全問題,當(dāng)線程作為為寫入方和線程作為寫入方之間進(jìn)行交互和,線程作為寫入方和線程作為讀取方之間進(jìn)行交互,就會造成互斥。

線程對數(shù)據(jù)的訪問,主要存在三種情況:

  • 線程只是對數(shù)據(jù)進(jìn)行讀操作,此時自然不會出現(xiàn)線程不安全問題。
  • 多個線程對數(shù)據(jù)進(jìn)行寫操作,就會出現(xiàn)線程不安全問題。
  • 一個線程對數(shù)據(jù)進(jìn)行讀操作,另個線程對數(shù)據(jù)進(jìn)行寫操作,也會出現(xiàn)線程不安全問題。

簡單的來說,線程的讀操作,就是線程對數(shù)據(jù)進(jìn)行訪問。線程的寫操作,就是線程對數(shù)據(jù)進(jìn)行修改。讀一下問題不大,但寫一下就難免會造成意外。因此,我們有了 讀寫鎖 這個概念。

讀寫鎖,就是把讀和寫這兩個操作分開來加鎖這樣就能避免互斥。Java 標(biāo)準(zhǔn)庫提供了一個 ReentrantReadWriteLock 類,實現(xiàn)了讀寫鎖。

ReentrantReadWriteLock.ReadLock 類表示一個讀鎖.。這個對象提供了 lock / unlock 方法進(jìn)行加鎖解鎖。

ReentrantReadWriteLock.WriteLock 類表示一個寫鎖.。這個對象也提供了 lock / unlock 方法進(jìn)行加鎖解鎖。

  • 讀加鎖與讀加鎖之間,不互斥
  • 寫加鎖與寫加鎖之間,互斥
  • 讀加鎖與寫加鎖之間,互斥

互斥,就會操作線程的掛起等待,一旦線程掛起等待了,就不知道什么時候能夠被喚醒了。因此,我們在編寫代碼的時候盡可能減少互斥。

讀寫鎖特別適用于“頻繁讀,不頻繁寫”的場景中。比如,學(xué)校的教務(wù)系統(tǒng):

假設(shè)計算機(jī)軟件專業(yè)的學(xué)生有 300 個同學(xué),這300個同學(xué)幾乎每天都要課程表為了防止課表更改,這樣的一個操作就是頻繁讀(訪問)。

有特殊情況,老師生病了或是怎樣,偶爾會調(diào)課到其他時間點。這樣的操作,就是不頻繁寫(修改)。

注意,synchronized 不是讀寫鎖。

3. 重量級鎖與輕量級鎖

在并發(fā)編程中,輕量級鎖和重量級鎖是兩種鎖的實現(xiàn)方法,主要用于解決多個線程同時訪問共享資源時的同步問題。

輕量級鎖通常用于鎖競爭不激烈的情況下,通過在線程內(nèi)部使用CAS操作來進(jìn)行加鎖和解鎖,這種方式不需要進(jìn)行線程的上下文切換,因此性能比重量級鎖更高。但是,如果鎖競爭激烈的話,輕量級鎖的性能優(yōu)勢就不明顯了。

重量級鎖通常用于鎖競爭激烈的情況下,通過將競爭鎖的線程掛起并切換到內(nèi)核態(tài)來進(jìn)行加鎖和解鎖。由于需要進(jìn)行線程的上下文切換,因此性能比輕量級鎖更低。但是,在鎖競爭激烈的情況下,重量級鎖的效果要比輕量級鎖好得多,因為它可以有效地避免鎖爭用問題,減少了線程的搶占和切換,從而提高了系統(tǒng)的效率和響應(yīng)速度。

synchronized 的輕量級鎖策略大概都是通過自旋鎖的方式實現(xiàn)的,重量級鎖則是掛起等待鎖。

4. 自旋鎖與掛起等待鎖

自旋鎖 VS 掛起等待鎖:

自旋鎖,當(dāng)線程之間進(jìn)行搶占鎖內(nèi)資源時候,線程1 已經(jīng)搶占到鎖,線程2 則會持續(xù)等待 線程1 鎖內(nèi)任務(wù)結(jié)束后再進(jìn)行搶占鎖資源,在這期間 線程2 持續(xù)處于阻塞等待狀態(tài)。

掛起等待鎖,當(dāng)掛起等待鎖遇到這種情況時,發(fā)現(xiàn)有線程已經(jīng)搶占到鎖了,則會放棄阻塞等待。直到鎖開放了,則再參與搶占鎖。

因此,自旋鎖有以下優(yōu)缺點:

  • 優(yōu)點:時刻占用系統(tǒng)資源,不涉及線程阻塞和調(diào)度,一旦鎖被釋放了,參與鎖的競爭。
  • 缺點:當(dāng)鎖內(nèi)任務(wù)比較復(fù)雜時,鎖被其他線程占有時間過長,那么就會持續(xù)消耗系統(tǒng)資源。
  • 掛起等待鎖則相反

4.1 自旋鎖

自旋鎖,按照正常的邏輯,當(dāng)線程搶占鎖時進(jìn)入阻塞狀態(tài),過不了多久鎖就被釋放了。因此,自旋鎖就沒必要放棄 CPU 了,一直占用著 CPU 的內(nèi)存空間。

自旋鎖偽代碼:

while(槍鎖lock == 失敗) {}

如果獲取鎖失敗,立即再嘗試獲取鎖,無限循環(huán)下去直到獲取到鎖為止。第一次獲取鎖失敗,往后的獲取鎖操作會在極短的時間內(nèi)到來,一旦鎖被其他線程釋放,就能第一時間獲取到鎖。這就是輕量級鎖的體現(xiàn)(鎖的競爭還不太激烈,嘗試使用自旋方式加鎖)。

4.2 掛起等待鎖

當(dāng)線程獲取鎖失敗后,并不會進(jìn)行阻塞等待。而隨著系統(tǒng)的調(diào)度,不占用 CPU 。直到鎖開發(fā)后,再嘗試參與鎖競爭。這種情況就是掛起等待鎖,也是重量級鎖的體現(xiàn)(鎖的競爭太激烈了,線程跟隨系統(tǒng)的調(diào)度)。

舉例:

自旋鎖與掛起等待鎖的現(xiàn)實生活體現(xiàn):張三是一個普通的男生,如花是一個漂亮的女孩,在此張三作為線程,如花作為鎖。

張三開始追求如花,但是如花已經(jīng)有男朋友了。張三又是個死皮賴臉的人。每天堅持給女孩發(fā)信息,期待著某一天如花分手,能得到如花。此時張三就處于自旋鎖的狀態(tài)。

隨著競爭的激烈,又有許多人想要追求如花。張三開始動搖了,開始努力敲代碼、認(rèn)真學(xué)習(xí)不參與追如花的競爭了(隨著系統(tǒng)的調(diào)度做其他事去了)。如果某一天如花變?yōu)閱紊砹?,系統(tǒng)會通知張三如花單身了(鎖空閑了),張三就又開始參與競爭鎖。此時張三的狀態(tài)就是掛起等待鎖狀態(tài)。

5. 公平鎖與非公平鎖

公平鎖與非公平鎖講究四個字“公平競爭”,假設(shè)有三個線程搶占鎖資源,當(dāng)鎖被釋放后就會出現(xiàn)兩種情況:公平競爭鎖、非公平競爭鎖。

公平鎖:遵循先來后到的原則,線程1 進(jìn)入鎖,鎖釋放后。線程2 進(jìn)入鎖,鎖再釋放后。線程3 進(jìn)入鎖。整個過程是按照順序執(zhí)行的。

非公平鎖:由于線程之間搶占資源,導(dǎo)致鎖被無序的搶占。這樣 3 個線程都有機(jī)會優(yōu)先進(jìn)入鎖。整個過程會造成無序執(zhí)行。

通過上圖我們就能很好的理解,公平鎖與非公平鎖之間的差異。當(dāng)然,線程的調(diào)度是隨機(jī)的因此多個線程競爭鎖時可以隨意進(jìn)行搶占“手快有,手慢無”(非公平鎖)。要想實現(xiàn)公平鎖,我們可以使用一些特定的數(shù)據(jù)結(jié)構(gòu)來達(dá)到按順序使用鎖。

在實際開發(fā)中,公平鎖與非公平鎖沒有好壞之分,我們按照需求來進(jìn)行設(shè)置。注意,synchronized 屬于非公平鎖。

6. 可重入鎖與不可重入鎖

可重入鎖即允許一個線程多次獲取同一把鎖。

不可重入鎖是指一旦線程獲得了該鎖,此時再次請求獲取該鎖時,系統(tǒng)會將該線程掛起,直到該鎖被釋放為止。因此,不可重入鎖不能再同一線程中重復(fù)獲取。

可重入鎖是指當(dāng)一個線程獲得了該鎖之后,在該鎖還未釋放之前,可以再次獲取該鎖。這種鎖可以防止死鎖的發(fā)生,因為在獲取之后可以在方法中重新獲取該鎖,從而避免死鎖的發(fā)生。

Java 中的 synchronized 關(guān)鍵字是一種可重入鎖,而 ReentrantLock 是 Java 中常用的可重入鎖類,synchronized 不需要手動解鎖,而 ReentrantLock 需要手動解鎖。

需要注意的是,可重入鎖雖然提高了代碼的靈活性和可維護(hù)性,但同時也可能會帶來出現(xiàn)深度嵌套鎖的風(fēng)險,引發(fā)死鎖或性能下降等問題。因此,在使用可重入鎖時需要仔細(xì)設(shè)計和管理。

談?wù)勀銓ynchronized的演變過程的理解?

synchronized 既是悲觀鎖也是樂觀鎖,synchronized 即是輕量級鎖也是重量級鎖,synchronized 即是自旋鎖也是掛起等待鎖,synchronized 不是讀寫鎖,synchronized 是非公平鎖,synchronized是可重入鎖。

synchronized 的初始化的時候是一個樂觀鎖/輕量級鎖/自旋鎖,隨著synchronized的競爭激烈會升級為悲觀鎖/重量級鎖/掛起等待鎖,另外輕量級鎖是部分基于自旋鎖、重量級鎖是部分基于掛起等待鎖。

在鎖的策略中還會引申到“死鎖”的概念,在下篇博文中,我會介紹。大家也可以通過下方專欄中搜索多線程相關(guān)內(nèi)容。

到此這篇關(guān)于Java多線程中常見的鎖策略詳解的文章就介紹到這了,更多相關(guān)Java常見的鎖策略內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論