Java中synchronized同步块的执行流程
必要知識
Java 對象的數(shù)據(jù)結(jié)構(gòu)
- 在 HotSpot 虛擬機(jī)中,Java 對象在內(nèi)存中存儲(chǔ)的布局可以分為 3 塊區(qū)域:對象頭(Header)、實(shí)例數(shù)據(jù)(Instance Data)和對齊填充(Padding)
- 對象頭中的 Mark Word 部分,用于存儲(chǔ)對象自身的運(yùn)行時(shí)數(shù)據(jù),如哈希碼(HashCode)、GC 分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程 ID、偏向時(shí)間戳等,其中的最后 2bit 是鎖狀態(tài)標(biāo)志位(無鎖、偏向鎖、輕量級鎖、重量級鎖、GC 標(biāo)識)
鎖升級流程
無鎖 -> 偏向鎖 -> 輕量級鎖 -> 重量級鎖
無鎖: 沒有對資源進(jìn)行鎖定,所有的線程都能訪問并修改同一個(gè)資源,但同時(shí)只有一個(gè)線程能修改成功,其他修改失敗的線程會(huì)不斷重試直到修改成功。
偏向鎖:對象的代碼一直被同一線程執(zhí)行,不存在多個(gè)線程競爭,該線程在后續(xù)的執(zhí)行中自動(dòng)獲取鎖,降低獲取鎖帶來的性能開銷。偏向鎖,指的就是偏向第一個(gè)加鎖線程,該線程是不會(huì)主動(dòng)釋放偏向鎖的,只有當(dāng)其他線程嘗試競爭偏向鎖才會(huì)被釋放。
偏向鎖的撤銷,需要在某個(gè)時(shí)間點(diǎn)上沒有字節(jié)碼正在執(zhí)行時(shí),先暫停擁有偏向鎖的線程,然后判斷鎖對象是否處于被鎖定狀態(tài)。如果線程不處于活動(dòng)狀態(tài),則將對象頭設(shè)置成無鎖狀態(tài),并撤銷偏向鎖;
如果線程處于活動(dòng)狀態(tài),升級為輕量級鎖的狀態(tài)。
輕量級鎖(樂觀鎖):輕量級鎖是指當(dāng)鎖是偏向鎖的時(shí)候,被第二個(gè)線程 B 所訪問,此時(shí)偏向鎖就會(huì)升級為輕量級鎖,線程 B 會(huì)通過自旋的形式嘗試獲取鎖,線程不會(huì)阻塞,從而提高性能。
當(dāng)前只有一個(gè)等待線程,則該線程將通過自旋進(jìn)行等待。但是當(dāng)自旋超過一定的次數(shù)時(shí),輕量級鎖便會(huì)升級為重量級鎖;當(dāng)一個(gè)線程已持有鎖,另一個(gè)線程在自旋,而此時(shí)又有第三個(gè)線程來訪時(shí),輕量級鎖也會(huì)升級為重量級鎖。
重量級鎖(悲觀鎖):指當(dāng)有一個(gè)線程獲取鎖之后,其余所有等待獲取該鎖的線程都會(huì)處于阻塞狀態(tài)。
重量級鎖通過對象內(nèi)部的監(jiān)視器(monitor)實(shí)現(xiàn),而其中 monitor 的本質(zhì)是依賴于底層操作系統(tǒng)的 Mutex Lock 實(shí)現(xiàn),操作系統(tǒng)實(shí)現(xiàn)線程之間的切換需要從用戶態(tài)切換到內(nèi)核態(tài),切換成本非常高。
synchronized 的執(zhí)行過程
在所有的鎖都啟用的情況下,線程進(jìn)入臨界區(qū)時(shí)會(huì)先去獲取偏向鎖,如果已經(jīng)存在偏向鎖了,則會(huì)嘗試獲取輕量級鎖,啟用自旋鎖,如果自旋也沒有獲取到鎖,則使用重量級鎖,沒有獲取到鎖的線程阻塞掛起,直到持有鎖的線程執(zhí)行完同步塊喚醒他們
總結(jié)
以上是生活随笔為你收集整理的Java中synchronized同步块的执行流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 画蛇添足的寓意是什么 画蛇添足的故事告诉
- 下一篇: Java版大顶堆的实现