java多線程之二——Synchronized

在java多線程并發(fā)編程中,Synchronized一直占有很重要的角色猛遍。Synchronized通過獲取鎖來實(shí)現(xiàn)同步。先來看一下,它的使用方法:

package com.Vinctor.Tst;

public class VinctorSyncDemo {

   public static synchronized void staticSyncMethod() {
       System.out.println("static synchronized");
   }

   public synchronized void normalSyncMethod() {
       System.out.println("normal  SyncMethod");
   }

   public void normalInnerMethod() {
       synchronized (this) {
           
       }
   }

   public void staticInnerMethod() {
       synchronized (VinctorSyncDemo.class) {

       }
   }
}

如上,分為三種方式:

  • staticSyncMethod懊烤,修飾靜態(tài)方法梯醒,鎖是當(dāng)前類的類對象,位于方法區(qū)
  • normalSyncMethod腌紧,修飾普通實(shí)例方法茸习,鎖是當(dāng)前實(shí)例對象,位于堆
  • 修飾代碼塊壁肋,如normalInnerMethodstaticInnerMethod号胚,其中synchronized (this)normalSyncMethod效果相同,synchronized (VinctorSyncDemo.class)staticSyncMethod效果相同浸遗。

我們將上述代碼javap之后猫胁,看一下字節(jié)碼指令:

image.png

可以看到看到staticInnerMetho方法在執(zhí)行到synchronized代碼塊時(shí),有兩個(gè)指令monitorentermonitorexit跛锌,而下面異常表指向的位置10弃秆,也同樣執(zhí)行了monitorexit∷杳保可見同步代碼塊的實(shí)現(xiàn)是使用monitorentermonitorexit兩個(gè)代碼塊進(jìn)行獲取鎖已釋放鎖的菠赚,發(fā)生異常之后,也同樣會(huì)釋放鎖氢卡。JVM 必須保證monitorentermonitorexit相對應(yīng)锈至。當(dāng)監(jiān)視器一個(gè)線程被持有時(shí)晨缴,那么這個(gè)線程就持有了鎖译秦,其他線程就不能獲取這個(gè)監(jiān)視器,直至monitorexit釋放鎖击碗。同步方法同樣也可以用著兩個(gè)指令獲取和釋放鎖筑悴。

當(dāng)一個(gè)線程試圖訪問同步方法或者同步代碼塊時(shí),首先需要獲取鎖稍途,退出同步方法或者同步代碼塊時(shí)需要釋放鎖阁吝。可以看出鎖在Synchronized中起到至關(guān)重要的作用械拍。鎖是什么呢突勇?他是怎么存儲(chǔ)的呢?
通過以前的文章坷虑,我們了解到實(shí)例對象存儲(chǔ)在堆中甲馋,類對象存儲(chǔ)在方法中,一個(gè)對象區(qū)域包括:對象頭迄损,對象數(shù)據(jù)定躏,對其數(shù)據(jù)。此處對象頭中存儲(chǔ)著Synchronized的鎖。對象頭中有一個(gè)稱為Mark Word的區(qū)域痊远,存儲(chǔ)著對象的 hashCode垮抗,分代年齡和鎖標(biāo)記位。如圖:

image.png

運(yùn)行期間碧聪,mark word 中存儲(chǔ)的數(shù)據(jù)鎖的標(biāo)記位的變化而變化冒版,其可能變化情況如下:

image.png

可以看到,分為很多鎖:偏向鎖逞姿,輕量級(jí)鎖壤玫,重量級(jí)鎖。

鎖的分類

內(nèi)置鎖按照狀態(tài)分為:無鎖狀態(tài)哼凯,偏向鎖欲间,輕量級(jí)鎖,重量級(jí)鎖断部。四中狀態(tài)隨著鎖的競爭加劇而升級(jí)猎贴,但是不是回退降級(jí)。(本文僅討論 HotSpot)

鎖的升級(jí)

虛擬機(jī)開發(fā)人員研究發(fā)現(xiàn)蝴光,大多數(shù)情況下她渴,多線程存在競爭的情況很少,為了避免同一線程多次進(jìn)行鎖的獲取蔑祟,故引入了偏向鎖的概念趁耗。

當(dāng)一個(gè)線程A訪問同步代碼塊的時(shí)候,
首先檢查鎖標(biāo)志位:如果為01(無鎖或偏向鎖)疆虚,再檢查是否為偏向鎖苛败,

  • 如果為0(不是偏向鎖),會(huì)通過 CAS 操作(下面將解釋)在對象頭中存儲(chǔ)當(dāng)前訪問的線程 A 的ID 径簿;
  • 如果為1(是偏向鎖)罢屈,檢查一下對象頭中是否是當(dāng)前線程A 的 ID,如果是篇亭,則表示獲取到鎖缠捌,執(zhí)行代碼塊。接下文译蒂,
    接上文曼月,如果不是當(dāng)前線程A 的 ID,則嘗試使用 CAS替換線程 ID柔昼,如果成功哑芹,則表示獲取鎖;如果失敗岳锁,則表示其他線程正在持有鎖绩衷,這時(shí)出現(xiàn)了競爭蹦魔。我們假設(shè)當(dāng)前線程 B 持有該鎖。出現(xiàn)了競爭咳燕,我們這時(shí)需要撤銷線程 B 的偏向鎖(解鎖)勿决。
    當(dāng)線程 B 運(yùn)行到安全點(diǎn)或者安全區(qū)域的時(shí)候,線程 B 暫停招盲,這是檢查線程 B是否已經(jīng)退出了同步代碼塊低缩,
  • 如果線程 B已經(jīng)退出了同步代碼塊,則解鎖曹货,將對象頭 的線程 ID 清空咆繁,是否偏向鎖標(biāo)記位0(設(shè)為無鎖狀態(tài)),線程 A 繼續(xù)通過 CAS 獲取鎖顶籽。
  • 如果線程 B 沒有退出同步代碼塊玩般,則表示線程 A 不能獲取鎖,這時(shí)鎖升級(jí)礼饱,升級(jí)為輕量級(jí)鎖坏为。

鎖升級(jí)輕量級(jí)鎖之后,對象頭中不在存儲(chǔ)線程 ID 等信息镊绪,而是將這些信息拷貝至持有鎖的線程棧中鎖記錄中匀伏,再將對象頭指向該地址。上面的例子??中蝴韭,

  • 線程 B 持有鎖够颠,在線程 B 的棧中分配鎖記錄,并將對象頭數(shù)據(jù)拷貝進(jìn)去榄鉴,這時(shí)鎖的標(biāo)志位為00(輕量級(jí)鎖)履磨,并將對象頭指針指向該線程 B 的棧,線程 B 喚醒牢硅,并繼續(xù)執(zhí)行代碼蹬耘;
  • 此時(shí)線程 A 也是分配鎖記錄,并拷貝對象頭中 Mark Word减余,但是線程 A 還是不能獲取到鎖。
    這時(shí)線程 A還是進(jìn)行 CAS 操作惩系,企圖將對象頭指向自己的鎖記錄位岔,
  • 如果替換成功,則表示線程 A 獲取到了鎖堡牡,執(zhí)行同步代碼塊抒抬;
  • 如果還是不成功,這時(shí)線程 A 將執(zhí)行自旋CAS(自旋:顧名思義晤柄,自己轉(zhuǎn)著玩兒擦剑,也就是線程 A 不暫停等待,也不阻塞,而是執(zhí)行一些無用的代碼惠勒,此時(shí)空轉(zhuǎn)赚抡,也占用 CPU 時(shí)間,可以想象一個(gè)while 循環(huán)纠屋,目的就是稍等一下涂臣,看看持有鎖的線程是是否很快就釋放鎖),自旋的過程中嘗試 CAS 替換對象頭指針售担,當(dāng)自旋一定數(shù)目之后赁遗,線程 A 還是沒有獲取到鎖(夠悲催的),這時(shí)鎖升級(jí)族铆,升級(jí)為重量級(jí)鎖岩四,此時(shí)標(biāo)志位10。升級(jí)為重量級(jí)鎖之后哥攘,線程 A 這是不在爭搶資源炫乓,而是掛起當(dāng)前線程,等待其他線程釋放鎖之后將它喚醒献丑。

擁有輕量級(jí)鎖的線程執(zhí)行完同步代碼塊后末捣,需要解鎖輕量級(jí)鎖,這是還是需要使用 CAS 操作创橄,將棧中鎖記錄替換會(huì)對象頭箩做,如果成功,表示沒有競爭妥畏;如果失敗邦邦,表示存在競爭,其他線程嘗試獲取過鎖醉蚁,那就需要在釋放鎖的過程中燃辖,喚醒被掛起的線程。

貼一張收藏的流程圖(出處不明网棍,如侵權(quán)黔龟,請告知):


點(diǎn)擊可放大,可查看原圖

一個(gè)??

以上就是鎖升級(jí)的過程滥玷,比較亂氏身,舉個(gè)現(xiàn)實(shí)生活的栗子,上廁所惑畴。廁所幾位鎖(對象)蛋欣。

為了方便對比,我們定一個(gè)規(guī)則如贷,當(dāng)一個(gè)人上廁所時(shí)陷虎,需要將自己的牌子掛在廁所的門上到踏,上完廁所,從廁所出來就不必摘下牌子尚猿,下次再上的時(shí)候就不需要掛了窝稿,只是看一下這個(gè)牌子是不是自己的就可以了。此為偏向鎖谊路。

當(dāng)你再上廁所的過程中讹躯,小明過來了也想上廁所,就看到了牌子缠劝,不是自己的潮梯,想要把牌子換成自己的,但是這是你還在上廁所惨恭,沒辦法秉馏,小明只能在廁所門前晃來晃去,等著你上完廁所出來脱羡。如果你能在短時(shí)間里出來萝究,那小明就進(jìn)去上廁所了。此時(shí)為輕量級(jí)鎖锉罐。

但是萬一你鬧肚子帆竹,小明也不可能一直在外面等著,這是他就選擇回座位等著脓规,并告訴你一聲:“哥們栽连,上完告訴我一聲!”侨舆,這時(shí)的小明不在主動(dòng)去想要獲取鎖秒紧,而是等著你上完廁所出來喊他,他才上廁所挨下。這是即為重量級(jí)鎖熔恢。

CAS

CAS,Compare and Swap即比較并替換臭笆,設(shè)計(jì)并發(fā)算法時(shí)常用到的一種技術(shù)叙淌。java 中的原子類以及concurrent包大量使用了該技術(shù)進(jìn)行原子操作。

CAS有三個(gè)操作數(shù):內(nèi)存值V耗啦、舊的預(yù)期值A(chǔ)凿菩、要修改的值B,當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí)帜讲,將內(nèi)存值修改為B并返回true,否則什么都不做并返回false

JDK中有一個(gè)類Unsafe(sun.misc.Unsafe)椒拗,它提供了硬件級(jí)別的原子操作似将。Unsafe類中的一個(gè)方法如下:

public final native boolean compareAndSwapInt(Object o, long offset,
                                              int expected,
                                              int x);

此為 native 方法获黔,JVM 會(huì)將此方法映射為cmpxchgCPU 指令,該指令為原子操作在验,故多用于多線程環(huán)境中而不會(huì)產(chǎn)生數(shù)據(jù)錯(cuò)誤玷氏。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市腋舌,隨后出現(xiàn)的幾起案子盏触,更是在濱河造成了極大的恐慌,老刑警劉巖块饺,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赞辩,死亡現(xiàn)場離奇詭異,居然都是意外死亡授艰,警方通過查閱死者的電腦和手機(jī)辨嗽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淮腾,“玉大人糟需,你說我怎么就攤上這事」瘸” “怎么了洲押?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長圆凰。 經(jīng)常有香客問我杈帐,道長,這世上最難降的妖魔是什么送朱? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任娘荡,我火速辦了婚禮,結(jié)果婚禮上驶沼,老公的妹妹穿的比我還像新娘炮沐。我一直安慰自己,他們只是感情好回怜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布大年。 她就那樣靜靜地躺著,像睡著了一般玉雾。 火紅的嫁衣襯著肌膚如雪翔试。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天复旬,我揣著相機(jī)與錄音垦缅,去河邊找鬼。 笑死驹碍,一個(gè)胖子當(dāng)著我的面吹牛壁涎,可吹牛的內(nèi)容都是我干的凡恍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼怔球,長吁一口氣:“原來是場噩夢啊……” “哼嚼酝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起竟坛,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對情侶失蹤闽巩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后担汤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涎跨,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年漫试,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了六敬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驾荣,死狀恐怖外构,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情播掷,我是刑警寧澤审编,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站歧匈,受9級(jí)特大地震影響垒酬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜件炉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一勘究、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斟冕,春花似錦口糕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秀撇,卻和暖如春超棺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呵燕。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工棠绘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓弄唧,卻偏偏與公主長得像适肠,于是被迫代替她去往敵國和親霍衫。 傳聞我的和親對象是個(gè)殘疾皇子候引,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容