synchronized關(guān)鍵字的原理刨析
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時間送達(dá)
66套java從入門到精通實(shí)戰(zhàn)課程分享
前言
關(guān)于synchronized原理解析,我們首先要分析一下對象都有哪些東西,對象頭到底存儲了什么,synchronzied關(guān)鍵字到底是如何進(jìn)行鎖膨脹的,在使用過程中同步方法塊和同步代碼塊到底有什么區(qū)別,在回頭看synchronized的使用。針對的時JDK1.6之后的版本的synchronized深度分析。
前期準(zhǔn)備
為輸出對象頭導(dǎo)入jar
<dependency>
????<groupId>org.openjdk.jolgroupId>
????<artifactId>jol-coreartifactId>
????<version>0.11version>
dependency>設(shè)置偏向鎖的啟動延遲
#關(guān)閉偏向鎖(為什么要關(guān)閉偏向鎖,有什么好處嘛,這個問題先不做回答)
-XX:-UseBiasedLocking
#設(shè)置偏向鎖的一個啟動延遲
-XX:BiasedLockingStartupDelay=0對象
對象中有哪些部分
新建一個對象,進(jìn)行main方法輸出
public?class?A?{
???int?status=0;
???boolean?flag=true;
}public?static?void?main(String[] args) {
????a=new?A(); System.out.println(ClassLayout.parseInstance(a).toPrintable());
}輸出的一個對象信息(當(dāng)前的操作系統(tǒng)是一個64位的,32位的是不一樣的)

從這個上面看,我們可以分析出包含96bit(12byte*8)的對象頭和兩個屬性(status,flag)字段屬性。這些都屬于對象數(shù)據(jù),int 類型的數(shù)據(jù)我們知道,占用4個byte,1個boolean值得可以使用1個byte去表示,那么為什么后面還出現(xiàn)了7個byte,這是因?yàn)閖vm在分配內(nèi)存的時候只能是8的一個倍數(shù)。所以就出現(xiàn)了7個byte。
所以我們小小的總結(jié)一下。
一個對象中包含對象頭,實(shí)列數(shù)據(jù),對象填充.

對象頭有哪些東西
通過文檔查找來驗(yàn)證
jvm底層一個是基于C/C++來實(shí)現(xiàn)的,查看版本java -version 可以知道jvm的實(shí)現(xiàn)是HotSport來實(shí)現(xiàn)的jvm規(guī)范和標(biāo)準(zhǔn)的。通過openjdk的官方文檔 [https://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html]查看對于對象頭的一個定義。

說了一下包含兩部分,mark word 和 klass pointer(這個對象對應(yīng)的class類指針地址) 兩部分。在open Jdk的源碼中有一個markOop的文件中說明了在64bit的操作系統(tǒng)中,mark word 占用了64位的。所以我們得出一個結(jié)論。

那么此時我們知道了,對象頭中包含了 這個對象對應(yīng)的class類的類型,Gc age,hashcode , synchronized關(guān)鍵字的同步狀態(tài)。
對象頭再深入分析

這里的klass word 為什么是32bit呢,因?yàn)樵趈vm中如果開啟了指針壓縮(1.8默認(rèn)開啟的)就會對對象頭進(jìn)行壓縮。所以看到是32bit,如果關(guān)閉就會看到是64bit。
關(guān)閉指針壓縮
-XX:-UseCompressedOops鎖的一個狀態(tài)分析
new 對象A
public?static?void?main(String[] args) {
???a=new?A();
???System.out.println(ClassLayout.parseInstance(a).toPrintable());
???a.hashCode();
???System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
第一次打印這個對象A的情況(沒有計(jì)算a的hashcode)無鎖可偏向,就是沒有線程來獲取這把鎖,也沒有計(jì)算hashcode。

全部為0的就是沒有HashCode 或者 線程ID 是空的。
第二次打印這個對象A的情況(計(jì)算了a的hashcode),無鎖不可偏向,沒有線程來獲取這把鎖,但是計(jì)算了hashcode。
并設(shè)置了HashCode 到mark word 中。
當(dāng)我們的代碼變成這樣下面這樣時,我們在看這個對象頭的中mark word 的一個鎖的狀態(tài)。
public?static?void?main(String[] args) {
???a=new?A();
???System.out.println(ClassLayout.parseInstance(a).toPrintable());
???a.hashCode();
???System.out.println(ClassLayout.parseInstance(a).toPrintable());
???lock();
???new?Thread(()->{
??????lock();
???}).start();
}
public??static??void??lock(){
???synchronized (a){
??????System.out.println("線程名稱=="+Thread.currentThread().getName());
??????System.out.println(ClassLayout.parseInstance(a).toPrintable());
???}
}main線程來進(jìn)行加鎖操作,main線程第一次來加鎖,直接變成了輕量級鎖,這是為什么呢,不應(yīng)該是偏向鎖嘛。
這是因?yàn)椋簩@個對象A進(jìn)行了一個hashcode計(jì)算,那么hashcode占據(jù)了56個bit,鎖的一個狀態(tài)就變成了無鎖,不可偏向狀態(tài)。沒有辦法進(jìn)行偏向,第一個線程來獲取鎖的時候就會變成輕量級鎖。

此時我們把計(jì)算hashcode注釋掉
public?static?void?main(String[] args) {
???a=new?A();
???System.out.println(ClassLayout.parseInstance(a).toPrintable());
?
???lock();
???new?Thread(()->{
??????lock();
???}).start();
}
public??static??void??lock(){
???synchronized (a){
??????System.out.println("線程名稱=="+Thread.currentThread().getName());
??????System.out.println(ClassLayout.parseInstance(a).toPrintable());
???}
}
————————————————
版權(quán)聲明:本文為CSDN博主「只穿T恤的程序員」的原創(chuàng)文章,遵循CC 4.0?BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_36114346/article/details/107488920運(yùn)行代碼得到一個 main線程加鎖之后,對象頭中的鎖的一個狀態(tài)變成了無鎖 可偏向,但是多了一個線程ID。

線程Thread-0運(yùn)行的一個情況,此時線程Thread-0變成了輕量級鎖,線程ID為thread-0,發(fā)生了一個鎖的膨脹。
但是先需要撤銷偏向鎖,再把鎖的狀態(tài)變成輕量級鎖,而輕量級鎖是一個CAS操作。相對撤銷偏向鎖來說來消耗的性能要低的多。

當(dāng)再執(zhí)行一個線程Thread-1時,此時就變成了重量級鎖。從輕量級鎖變成了重量級鎖。

到這里我們分析完了一個鎖的狀態(tài),包括鎖膨脹情況。
畫圖總結(jié)鎖的一個膨脹
沒有進(jìn)行HashCode運(yùn)算的流程

進(jìn)行過HashCode運(yùn)算的流程

基本使用
synchronzied加鎖的對象為A,叫對象鎖 ,修飾的是代碼塊,進(jìn)入同步代碼塊之前要獲取鎖。
public??static??void??lock(){
???synchronized (a){
??????System.out.println("線程名稱=="+Thread.currentThread().getName());
??????System.out.println(ClassLayout.parseInstance(a).toPrintable());
???}
}synchronzied在靜態(tài)方法上加鎖,加鎖的類型是這個方法對應(yīng)的類鎖。進(jìn)入同步方法之前要獲取鎖。
public???static????synchronized??void??lock2(){
}synchronzied在實(shí)列方法上加鎖,加鎖的類型是這個方法對應(yīng)的對象實(shí)列鎖。進(jìn)入同步方法之前要當(dāng)前對象實(shí)列鎖。
public?????synchronized??void??lock2(){
}————————————————
版權(quán)聲明:本文為CSDN博主「只穿T恤的程序員」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/weixin_36114346/article/details/107488920


??? ?
感謝點(diǎn)贊支持下哈?
