java互斥锁的实现原理_java-深入分析synchronized原理
互斥鎖
互斥鎖futex,全拼fast userspace mutexes,直翻為快速用戶空間互斥器,它是我們上層應(yīng)用實(shí)現(xiàn)鎖的最常用方法。futex是一塊所有進(jìn)程都可以訪問(wèn)的內(nèi)存,是通過(guò)cpu的原子操作修改內(nèi)存中的值來(lái)嘗試獲取瑣,如果沒(méi)有競(jìng)爭(zhēng),則直接在用戶空間完成操作,無(wú)需切換內(nèi)核空間,以此保證了futex的性能。
synchronized
其實(shí)java多線程操作大體就那么幾種,基于cas的aqs,synchronized和volatile,這篇文章主要介紹下synchronized。
synchronized是java的關(guān)鍵字,在老版本的jdk中性能表現(xiàn)并不太好,所以有了很多基于cas的Lock,但最近幾版jdk都對(duì)synchronized做了很多優(yōu)化,以后synchronized也會(huì)作為jdk主推的鎖。
synchronized最主要的優(yōu)化就是引入了升級(jí)功能,升級(jí)主要分偏向鎖、輕量級(jí)鎖、重量級(jí)鎖。
synchronized因?yàn)橛猩?jí)和降級(jí),既不會(huì)直接粗暴的使用互斥鎖,也不會(huì)有cas鎖在超高并發(fā)下多次嘗試引起的性能問(wèn)題,所以相比juc的cas鎖,synchronized在大多數(shù)情況下性能更好。
synchronized的使用方法主要有3種
//加鎖到靜態(tài)方法上
public static synchronized void test();
//加鎖到實(shí)例方法上
public synchronized void test();
//加鎖到對(duì)象上
synchronized(this){
}
// 加鎖在靜態(tài)方法上同加鎖到類對(duì)象上
synchronized(Test.class){
}
// 加鎖到實(shí)例方法上同加鎖到當(dāng)前對(duì)象上
synchronized(this){
}
總體上說(shuō),不管是鎖類對(duì)象,還是鎖其它對(duì)象都是鎖到一個(gè)對(duì)象上了。
這張圖很重要,在下面會(huì)多次用到,就先放這里了。
偏向鎖
java為了支持鎖對(duì)象,在對(duì)象頭上做了上圖的設(shè)計(jì),markword是對(duì)象頭中的一部分,64位虛擬機(jī)占64bit,markword在不同等級(jí)鎖狀態(tài)下存儲(chǔ)的內(nèi)容是不同的,上圖是鎖處于不同狀態(tài)時(shí)markword存儲(chǔ)的內(nèi)容。
其實(shí)經(jīng)過(guò)大量測(cè)試在我們使用synchronized時(shí),大多數(shù)情況并沒(méi)有發(fā)生競(jìng)爭(zhēng),很多訪問(wèn)都發(fā)生在一個(gè)線程里,所以設(shè)計(jì)了偏向鎖,偏向鎖的意思就是鎖偏向某個(gè)線程。
當(dāng)我們嘗試給一個(gè)對(duì)象加鎖時(shí),會(huì)有幾種情況。
未偏向:如果lock為01時(shí),biased_lock為0,表示沒(méi)有線程持有這個(gè)對(duì)象的偏向鎖,線程會(huì)通過(guò)cas的方式修改對(duì)象頭獲取偏向鎖。
可重偏向:如果lock為01時(shí),biased_lock為1,表示對(duì)象被偏向鎖定,這時(shí)還會(huì)拿epoch與klass(可以理解為某個(gè)Class在虛擬機(jī)中對(duì)應(yīng)的對(duì)象)的mark_prototype的epoch作比較,如果不一致表示可重偏向,線程會(huì)通過(guò)cas的方式修改對(duì)象頭獲取偏向鎖。
已偏向:lock為01,biased_lock為1,epoch與mark_prototype的epoch相等表示鎖已偏向,會(huì)比較thread字段的threadId,如果一致表示持有鎖的是當(dāng)前線程,可以重入。
如果沒(méi)有獲取到偏向鎖,就需要進(jìn)行一個(gè)撤銷偏向鎖并升級(jí)偏向鎖的過(guò)程,這個(gè)過(guò)程比較消耗性能,所以當(dāng)某類對(duì)象,比如我們的User.class這個(gè)對(duì)象的實(shí)例有很多次撤銷(默認(rèn)值為40),虛擬機(jī)就會(huì)更新klass的epoch,表示可重偏向,這就是為啥鎖的是對(duì)象,判斷epoch是去klass判斷。
輕量鎖
當(dāng)未獲取到偏向鎖時(shí),需要通知持有偏向鎖的線程撤銷偏向鎖,競(jìng)爭(zhēng)線程則進(jìn)行一個(gè)輕量級(jí)鎖加鎖的過(guò)程。
偏向鎖的撤銷:持有鎖的線程進(jìn)入safepoint時(shí),判斷持有鎖的線程是否在加鎖狀態(tài),如果是則直接修改markword為輕量級(jí)鎖,否則釋放偏向鎖,表示未鎖定、
輕量級(jí)鎖加鎖:虛擬機(jī)會(huì)在當(dāng)前線程的棧幀中創(chuàng)建一個(gè)lock record,并拷貝markword到lockrecord,再通過(guò)cas把對(duì)象的markword改為偏向鎖,ptr_to_lock_record指向lockrecord,如果cas成功則表示成功獲取輕量級(jí)鎖,否則進(jìn)行自旋嘗試,就是常說(shuō)的自旋鎖。
輕量級(jí)鎖釋放:輕量級(jí)鎖釋放時(shí),只要把lockrecord中的markword替換回對(duì)象頭的markword就釋放成功了。
重量級(jí)鎖
當(dāng)自旋嘗試次數(shù)超過(guò)閾值(jvm控制的動(dòng)態(tài)值),鎖就會(huì)進(jìn)一步升級(jí),升級(jí)為重量級(jí)鎖。
升級(jí)為重量鎖后,線程就會(huì)出現(xiàn)等待、阻塞、喚醒等各種操作,就會(huì)涉及到用戶態(tài)和內(nèi)核態(tài)的切換,所以叫重量級(jí)鎖,重量級(jí)鎖的實(shí)現(xiàn)就是文中最開(kāi)始提到的futex互斥鎖實(shí)現(xiàn)的。
重量級(jí)鎖既然需要線程的管理機(jī)制,自然引入了管程(monitor),java的管程模式類似mesa。
contentionList: 所有想要競(jìng)爭(zhēng)的線程都要進(jìn)入的隊(duì)列,又叫cxq。
entrylist: 準(zhǔn)備競(jìng)爭(zhēng)的線程都在這個(gè)隊(duì)列,這個(gè)隊(duì)列只有空的時(shí)候才去cxq中拉去,cxq每次只會(huì)有一個(gè)進(jìn)去entrylist。
OnDeck:entrylist中的一個(gè)線程,一般為最前面的線程,只有OnDeck線程才會(huì)去競(jìng)爭(zhēng)鎖、
waitset:當(dāng)我們調(diào)用wait()方法時(shí),線程就會(huì)進(jìn)入waitset,被notity后直接進(jìn)入entrylist,所以被喚醒的線程比剛參與競(jìng)爭(zhēng)的線程優(yōu)先級(jí)更高。
總結(jié)
以上是生活随笔為你收集整理的java互斥锁的实现原理_java-深入分析synchronized原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: android 手机壁纸源码,Andro
- 下一篇: hive udf 分组取top1_Hiv