前言:
Synchronized對于Java小伙伴們不陌生了纫事,作為一個java日常開發(fā)中比較常見的的本地鎖刹衫,當然還有ReentrantLock可重入鎖,最初版本中 ReentrantLock 的性能是遠遠強于 Synchronized 的口糕,后續(xù)java在一次次的版本迭代中 對 Synchronized 進行了大量的優(yōu)化缅阳,直到 jdk1.6 之后,兩種鎖的性能已經(jīng)相差無幾景描,甚至 Synchronized 的自動釋放鎖會更好用十办。今天我們主要說一下Synchronized同步鎖
Synchronized使用:
使用Synchronized的時候也很方便有以下倆種方法:
1、直接貼在方法上(鎖住的是當前類的字節(jié)碼文件)
2超棺、貼在代碼塊兒上(鎖住的是對象)
//直接貼在方法上
public synchronized void test1(){
// TODO
}
//貼在代碼塊兒上
public void test2(){
synchronized (this){
//todo
}
}
那么在程序運行中Synchronized鎖的這一塊代碼發(fā)生了什么向族?
先上圖
image.png
- 在線程運行過程中,線程先會去搶對象的監(jiān)視器棠绘,這個監(jiān)視器是對象獨有的就相當于一把鑰匙件相,搶到了再扭,那么你就獲得了當前代碼塊的執(zhí)行權
- 其他沒有搶到的線程就會進入隊列(SynchronizedQueue)當中等待,等待當前線程執(zhí)行完后适肠,釋放鎖霍衫,再去搶監(jiān)視器;
- 最后當前線程執(zhí)行完畢后通知出隊然后繼續(xù)重復次過程
- 從 jvm 的角度來看 monitorenter 和 monitorexit 指令代表著代碼的執(zhí)行與結束 侯养。
SynchronizedQueue
- SynchronizedQueue 是一個比較特殊的隊列,它沒有存儲功能澄干,它的功能就是維護一組線程逛揩,其中每個插入操作必須等待另一個線程的移除操作,同樣任何一個移除操作都需要等待另一個線程的插入操作麸俘。因此隊列內部其實是沒有任何一個元素的辩稽,或者說容量為0;嚴格說并不是一個容器从媚。由于隊列沒有容量逞泄,因此不能進行peek操作,因為只有移除元素的時候才有元素拜效;
jdk1.6以前 Synchronized 是一個重量級鎖:
image.png
這就是為什么說喷众,Synchronized 是一個重量級鎖的原因, 因為每一次鎖的資源都是直接和 cpu 去申請的紧憾,而 cpu 的鎖數(shù)量是固定的 到千,當 cpu 鎖資源使用完后還會進行鎖等待,這是一個非常耗時的操作赴穗。
但是在jdk1.6憔四,針對代碼層面進行了大量的優(yōu)化,也就是我們常說的鎖升級的過程般眉。
image.png
- 無鎖:對象一開始就是無鎖的狀態(tài)了赵;
- 偏向鎖:相當于給對象貼了一個標簽(將自己的線程Id存入對象頭中),下次我在進來的時候甸赃,發(fā)現(xiàn)標簽就是我的柿汛,我就可以繼續(xù)使用了
- 自旋鎖:自旋鎖,說白了就是自旋辑奈,想象一下有一個廁所苛茂,里面有一個人在,你很想上但是只有一個坑位鸠窗,所以你只能徘徊等待妓羊,等那個人出來以后,你就可以使用了 稍计。 這個自旋是使用 cas 來保證原子性的躁绸。
- 重量級鎖:直接向 cpu 去申請申請鎖 ,其他的線程都進入隊列中等待。
鎖升級是什么時候發(fā)生的净刮?
- 偏向鎖:一個線程獲取鎖時會由無鎖升級為偏向鎖
- 自旋鎖:當產(chǎn)生線程競爭時由偏向鎖升級為自旋鎖,想象一下 while(true) ;
- 重量級鎖:當線程競爭到達一定數(shù)量或超過一定時間時剥哑,晉升為重量級鎖
鎖的信息是記錄在哪里的?
image.png
- 這張圖是對象頭中 markword 的數(shù)據(jù)結構 淹父,鎖的信息就是在這里存放的株婴,很清楚的表明了鎖在升級的時候鎖信息的變動, 其實就是通過二進制的數(shù)值暑认,來對對象進行一個標記困介,每個數(shù)值代表一種狀態(tài) 。
既然synchronized有鎖升級那么有鎖降級嗎蘸际?
- 在 HotSpot 虛擬機中是有鎖降級的座哩, 但是僅僅只發(fā)生在 STW 的時候 ,只有垃圾回收線程能夠觀測到它粮彤,也就是說根穷, 在我們正常使用的過程中是不會發(fā)生鎖降級的,只有在 GC 的時候才會降級导坟。
今天就講到這吧屿良,累啦,下回想起來的時候在講吧 喜歡的可以插個旗點贊一下