怎么做賭博網(wǎng)站代理承德seo
synchronized進(jìn)階原理
1.輕量級(jí)鎖
輕量級(jí)鎖的使用場(chǎng)景:如果一個(gè)對(duì)象雖然有多個(gè)線程訪問,但多線程訪問的時(shí)間是錯(cuò)開的(也就是沒有競(jìng)爭(zhēng)),那么可以使用輕量級(jí)鎖來優(yōu)化(如果出現(xiàn)競(jìng)爭(zhēng),操作系統(tǒng)會(huì)將輕量級(jí)鎖升級(jí)為重量級(jí)鎖)。輕量級(jí)鎖對(duì)使用者是透明的(由操作系統(tǒng)控制),即語(yǔ)法仍是synchronized。
假設(shè)有兩個(gè)方法同步塊,利用同一個(gè)對(duì)象加鎖
static final Object obj = new Object();
public static void method1(){synchronized(obj){//同步塊Amethod2();}
}
public static void method2(){synchronized(obj){//同步塊B}
}
創(chuàng)建鎖記錄(Lock Record)對(duì)象,每個(gè)線程的棧幀都會(huì)包含一個(gè)鎖記錄的結(jié)構(gòu),內(nèi)部可以存儲(chǔ)鎖定對(duì)象的Mark Word
讓鎖記錄中Object reference 指向鎖對(duì)象,并嘗試用cas替換Object的Mark Word,將Mark Word的值存入鎖記錄(01代表未上鎖,00代表上輕量級(jí)鎖)
如果cas替換成功,對(duì)象頭中存儲(chǔ)了鎖記錄地址和狀態(tài)00,表示由該線程給對(duì)象加鎖
如果cas失敗,有兩種情況
· 如果其它線程已經(jīng)持有了該Object的輕量級(jí)鎖,這時(shí)表明有競(jìng)爭(zhēng),進(jìn)入鎖膨脹過程
· 如果是自己執(zhí)行了synchronized鎖重入(自己又對(duì)synchronized加鎖了),那么再添加一條Lock Record作為重入的計(jì)數(shù)
當(dāng)退出synchronized代碼塊(解鎖時(shí))如果有取值為null的鎖記錄,表示有重入,這時(shí)重置鎖記錄,表示重入計(jì)數(shù)減一
當(dāng)退出synchronized代碼塊(解鎖時(shí))鎖記錄的值不為null,這時(shí)使用cas將Mark Word的值恢復(fù)給對(duì)象頭
· 成功,則解鎖成功
· 失敗,說明輕量級(jí)鎖進(jìn)行了膨脹或已經(jīng)升級(jí)為重量級(jí)鎖,進(jìn)入重量級(jí)鎖解鎖流程
2.鎖膨脹
如果在嘗試加輕量級(jí)鎖的過程中,CAS操作無(wú)法成功,這時(shí)一種情況就是有其他線程為此對(duì)象加上了輕量級(jí)鎖(有競(jìng)爭(zhēng)),這時(shí)需要進(jìn)行鎖膨脹,將輕量級(jí)鎖變?yōu)橹亓考?jí)鎖。
static Object obj = new Object();
public static void method1(){synchronized(obj){//同步塊}
}
當(dāng)Thread-1進(jìn)行輕量級(jí)鎖時(shí),Thread-0已經(jīng)對(duì)該對(duì)象加了輕量級(jí)鎖
這時(shí)Thread-1加輕量級(jí)鎖失敗,進(jìn)入鎖膨脹流程
· 即為Object對(duì)象申請(qǐng)Monitor鎖,讓Object指向重量級(jí)鎖地址
· 然后自己進(jìn)入Monitor的EntryList中 BLOCKED
當(dāng)Thread-0退出同步代碼塊解鎖時(shí),使用cas將Mark Word的值恢復(fù)給對(duì)象頭,失敗。這時(shí)會(huì)進(jìn)入重量級(jí)解鎖流程,即按照Monitor地址找到Monitor對(duì)象,設(shè)置Owner為null,喚醒EntryList中BLOCKED線程,從它們中競(jìng)爭(zhēng)出下一個(gè)與Owner關(guān)聯(lián)的線程
3.自旋優(yōu)化
重量級(jí)鎖競(jìng)爭(zhēng)的時(shí)候,還可以使用自旋鎖來進(jìn)行優(yōu)化,如果當(dāng)前線程自旋鎖成功(即這時(shí)候持鎖線程已經(jīng)退出了同步塊,釋放了鎖),這時(shí)當(dāng)前線程就可以避免阻塞。
自選重試成功的情況
自旋重試失敗的情況
在java6之后自旋鎖是自適應(yīng)的,比如對(duì)象剛剛的一次自旋操作成功過,那么認(rèn)為這次自旋成功的可能性會(huì)高,就多自旋幾次;反之,就少自旋甚至不自旋。自旋會(huì)占用CPU時(shí)間,單核CPU自旋就是浪費(fèi),多核CPU自旋才能發(fā)揮優(yōu)勢(shì)。java7之后不能控制是否開啟自旋功能。
4.偏向鎖
輕量級(jí)鎖在沒有競(jìng)爭(zhēng)時(shí)(就自己這個(gè)線程),每次重入仍需要執(zhí)行CAS操作。java6中引入了偏向鎖來做進(jìn)一步優(yōu)化:只有第一次使用CAS將線程ID設(shè)置到對(duì)象的Mark Word頭,之后發(fā)現(xiàn)這個(gè)線程ID是自己的就表示沒有競(jìng)爭(zhēng),不用重新CAS。以后只要不發(fā)生競(jìng)爭(zhēng),這個(gè)對(duì)象就歸該線程所有。
static final Object obj = new Object();
public static void m1(){synchronized(obj){//同步塊Am2();}
}
public static void m2(){synchronized(obj){//同步塊Bm3();}
}
public static void m3(){synchronized(obj){//同步塊C}
}
偏向狀態(tài)
對(duì)象頭:
biased_lock 用于記錄是否啟動(dòng)偏向鎖(0為未啟動(dòng))
thread 是線程id(操作系統(tǒng)給的,和java中自己設(shè)置的不同)
一個(gè)對(duì)象創(chuàng)建時(shí):
· 如果啟動(dòng)了偏向鎖(默認(rèn)開啟),那么對(duì)象創(chuàng)建后,markword值為0x05即最后3位為101,這時(shí)它的thread、epoch、age都是0
· 偏向鎖默認(rèn)是延遲的,不會(huì)在程序啟動(dòng)時(shí)立即生效,如果想避免延遲,可以加VM參數(shù) - XX:BiasedLockingStartupDelay=0來禁止延遲
· 如果沒有開啟偏向鎖,那么對(duì)象創(chuàng)建后,markword值為0x01即最后3位為001,這時(shí)它的hashcode、age都為0,第一次用到hashcode時(shí)才會(huì)賦值
測(cè)試禁用:在上面測(cè)試代碼運(yùn)行時(shí)在添加VM參數(shù) -XX: -UseBiasedLocking 禁用偏向鎖
測(cè)試hashCode:當(dāng)對(duì)用對(duì)象的hashCode()時(shí),偏向鎖會(huì)被撤銷,對(duì)象就會(huì)變成普通狀態(tài)(當(dāng)輕量級(jí)鎖對(duì)象調(diào)用hashCode()時(shí),hashCode會(huì)記錄在棧幀中的鎖記錄里,當(dāng)重量級(jí)鎖調(diào)用hashCode()時(shí),hashCode會(huì)存在monitor對(duì)象里)
撤銷偏向鎖-其它線程使用對(duì)象
當(dāng)有其他線程使用偏向鎖對(duì)象時(shí),會(huì)將偏向鎖升級(jí)為輕量級(jí)鎖。
撤銷偏向鎖-使用wait/notify
批量重偏向
如果對(duì)象雖然被多個(gè)線程訪問,但沒有競(jìng)爭(zhēng),這時(shí)偏向了線程T1的對(duì)象仍有機(jī)會(huì)重新偏向T2,重偏向會(huì)重置對(duì)象的Thread ID
當(dāng)撤銷偏向鎖閾值超過20次后,JVM會(huì)在給這些對(duì)象加鎖時(shí)重新偏向至加鎖線程。
批量撤銷
當(dāng)撤銷偏向鎖閾值超過40次后,JVM會(huì)覺得不該偏向。于是整個(gè)類的所有對(duì)象都會(huì)變?yōu)椴豢善虻?新建的對(duì)象也是不可偏向的。
鎖撤銷
java有即時(shí)編譯器(JIT),會(huì)對(duì)代碼進(jìn)行優(yōu)化,將一些無(wú)用的鎖優(yōu)化掉了