Java中的synchronized重量級鎖解析
1. 背景
在JDK1.6以前,synchronized 的工作方式都是這種重量級的鎖。它的實現(xiàn)原理就是利用 kernel 中的互斥量,mutex。主要是內(nèi)核中的mutex 能夠保證它是一個互斥的量。如果線程1拿到了 mutex,那么線程2就拿不到了。這是內(nèi)核幫我們保證的。
至于為什么可以,可以去了解一下內(nèi)核中的互斥量。
2. 為啥叫做重量級鎖
內(nèi)核需要去申請這個互斥量,必須要進入內(nèi)核態(tài)。也就是這里需要用戶態(tài),內(nèi)核態(tài)的切換。狀態(tài)的切換,開銷是比較大的。這就是重型鎖的一個弊端。對于重型鎖的一些主要的封裝都是在 c 語言中的 pthread 這樣一個庫中,比如mutex_init, mutex_lock 等。之所以叫重量級鎖,就是因為需要進入到內(nèi)核態(tài)。
3. 能否在用戶態(tài)實現(xiàn)一把互斥鎖
我們不需要進入到內(nèi)核態(tài)就能夠獲取到這樣的一把鎖,也就是在用戶態(tài)就可以實現(xiàn)一把鎖。
比如說,現(xiàn)在就有一把鎖,就叫做 state。0:表示未被使用,1 表示鎖被占用了。
lock 的實現(xiàn)
如果鎖當前是被占用的狀態(tài),那么程序就一直死循環(huán)。如果某個時刻,有一個線程把鎖釋放了,那么就退出死循環(huán),執(zhí)行下一行 state = 1。而且持有者=當前線程。也就是 state = 0的時候,就可以任務當前線程可以拿到這把鎖了。
unlock 的實現(xiàn)
釋放鎖的時候,首先需要判斷一下當前持有鎖的線程是不是當前線程。如果是當前線程,那么就將 state 置為0, 持有者 = null。
當前設計方式存在的問題
while(state==1); state = 1;其實可以認為這個是比較賦值倆步操作,先比較,符合條件了,再進行賦值。那么其實這不是原子性的。比如說在比較的時候,倆個線程同時進行了比較,這倆個線程同時發(fā)現(xiàn)state = 0,那么這倆個線程同時都去會執(zhí)行 state = 1的操作,這倆個線程都認為自己拿到了鎖,那么這個就產(chǎn)生了并發(fā)的問題了。
如何改進這個問題呢?
我們看到比較和賦值不是原子性的,在軟件層面,我們也無法保證這倆步的原子性。所以,計算機給我們提供了原語,也就是 CAS。=
那么我們來看一下如何改進呢? 我們把比較賦值這倆步操作,變成了一個原語操作,CAS。比較并交換。CAS中有三個參數(shù),cas(state,0,1)。比較state的當前值是不是0,如果是0,那么就賦值為1。
再看看一下 unlock的操作。 在unlock操作中,其實也是一個比較賦值的操作,首先判斷當前持有者是不是當前線程,如果是,再進行賦值。
那為什么這里不需要用cas呢?因為必定是持有鎖的線程才能執(zhí)行到下一行。
而且賦值操作中,先賦值 持有者 = null。再賦值 state = 0。
因為如果先執(zhí)行 state = 0, 那么就相當于先釋放掉這把鎖了,另外一個線程就會執(zhí)行 lock 成功,拿到鎖,持有者 = 當前線程。
那么就可能會帶來一些問題。因為此時釋放掉的鎖并不是當前線程持有的鎖。
這種鎖也叫做自旋鎖。
自旋鎖的優(yōu)點與缺點
自旋鎖,spinLock。自旋鎖不需要進入到內(nèi)核態(tài),整個程序的執(zhí)行都是在用戶態(tài)的,包括cas。但是cas不是計算機的原語嗎?因為計算機的指令和用戶態(tài),內(nèi)核態(tài)沒有關(guān)系。
自旋鎖當然也有缺點。 lock 操作如果一直沒有拿到鎖的話,會一直嘗試。這是需要消耗cpu資源的。 所以自旋鎖對于鎖競爭比較激烈的情況下,是不適用的。
總結(jié)
mutex鎖和自旋鎖各有優(yōu)缺點,那么我們能不能把這倆者結(jié)合一下呢?
JDK 1.4 以前是借助內(nèi)核中的 Mutex 互斥量; JDK1.4 以后是利用自旋鎖,自旋n次以后,還是沒有拿到鎖的話,就切換到mutex。
也就是將自旋鎖和mutex進行了一個結(jié)合。因為mutex 加鎖失敗以后,會掛起,讓出cpu資源。這樣的話,算是對資源的一個合理利用。
JDK1.4以前,我們是可以設置自旋次數(shù)的,但是1.6以后,JDK可以自適應自旋,不用設置這個參數(shù)了。
當然現(xiàn)在我們所說的都是重量級鎖。包括mutext, 自旋鎖,自適應自旋鎖。
到此這篇關(guān)于Java中的synchronized重量級鎖解析的文章就介紹到這了,更多相關(guān)synchronized重量級鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot如何實現(xiàn)定時任務的動態(tài)增刪啟停詳解
這篇文章主要給大家介紹了關(guān)于Spring Boot如何實現(xiàn)定時任務的動態(tài)增刪啟停的相關(guān)資料,文中通過示例代碼以及圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2020-07-07Java數(shù)據(jù)結(jié)構(gòu)中關(guān)于AVL樹的實現(xiàn)方法詳解
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)中關(guān)于AVL樹的實現(xiàn)方法,AVL樹是高度平衡的二叉樹,它的特點是AVL樹中任何節(jié)點的兩個子樹的高度最大差別為1,本文主要給大家介紹了Java語言如何實現(xiàn)AVL樹,需要的朋友可以參考下2024-02-02java利用pdfbox+poi往pdf插入數(shù)據(jù)
這篇文章主要給大家介紹了關(guān)于java利用pdfbox+poi如何往pdf插入數(shù)據(jù)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-02-02chatgpt java環(huán)境調(diào)用源碼實現(xiàn)demo
這篇文章主要介紹了chatgpt java環(huán)境調(diào)用源碼實現(xiàn)demo,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02springboot整合redisson實現(xiàn)延時隊列(附倉庫地址)
延時隊列用于管理需要定時執(zhí)行的任務,對于大數(shù)據(jù)量和高實時性需求,使用延時隊列比定時掃庫更高效,Redisson提供一種高效的延時隊列實現(xiàn)方式,本文就來詳細的介紹一下,感興趣都可以了解學習2024-10-10Spring如何利用@Value注解讀取yml中的map配置
這篇文章主要介紹了Spring如何利用@Value注解讀取yml中的map配置,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02SpringBoot實現(xiàn)WebSocket服務并讓客戶端實時接收
使用SpringBoot和WebSocket可創(chuàng)建實時消息推送服務,首先添加WebSocket依賴至pom.xml,配置WebSocket端點和邏輯處理器,通過WebSocketHandler處理消息,使用AnnouncementController模擬消息推送,支持HTML和微信小程序客戶端接收消息,感興趣的可以了解一下2024-10-10