Java中synchronized關(guān)鍵字引出的多種鎖 問(wèn)題
前言
Java 中的 synchronized
關(guān)鍵字可以在多線程環(huán)境下用來(lái)作為線程安全的同步鎖。本文不討論 synchronized
的具體使用,而是研究下synchronized
底層的鎖機(jī)制,以及這些鎖分別的優(yōu)缺點(diǎn)。
一 synchronized機(jī)制
synchronized
關(guān)鍵字是JAVA中常用的同步功能,提供了簡(jiǎn)單易用的鎖功能。
synchronized
有三種用法,分別為:
用在普通方法上,能夠鎖住當(dāng)前對(duì)象。用在靜態(tài)方法上,能夠鎖住類(lèi)用在代碼塊上,鎖住的是synchronized
()里的對(duì)象
在JDK6之前,synchronized
使用的是重量級(jí)鎖制,在之后synchronized
加入了鎖膨脹機(jī)制,顯著提升了synchronized
關(guān)鍵字的效率。
基于synchronized
關(guān)鍵字,我們來(lái)了解下幾種類(lèi)別的鎖,并且講解synchronized
的鎖膨脹機(jī)制。
synchronized
鎖是非公平鎖。并且一個(gè)被synchronized
鎖住的對(duì)象或類(lèi),就是一把鎖。
另外一提,所有鎖都是存儲(chǔ)在Java對(duì)象頭里的,Java對(duì)象頭里的Mark Word里默認(rèn)存儲(chǔ)對(duì)象的HashCode,分代年齡和鎖標(biāo)記位。也就是說(shuō)Mark Word記錄了鎖的狀態(tài)
二 鎖膨脹機(jī)制與幾類(lèi)鎖
鎖膨脹是不可逆的
2.1 偏向鎖
synchronized
在JDK1.6以后默認(rèn)開(kāi)啟偏向鎖
,synchronized
最初都是偏向鎖
表現(xiàn):一個(gè)線程獲取鎖成功后,會(huì)在對(duì)象頭里記錄線程ID,以后該線程獲取和釋放鎖都沒(méi)有任何花費(fèi)。(因?yàn)樵撴i已經(jīng)被綁定在該線程上了,且在膨脹前不會(huì)改變),如果其他線程嘗試獲取這個(gè)鎖,偏向鎖
將會(huì)膨脹為輕量鎖
。
優(yōu)點(diǎn):在只有一個(gè)線程使用鎖的時(shí)候獲取和退出鎖沒(méi)有任何花費(fèi)
缺點(diǎn):鎖競(jìng)爭(zhēng)激烈會(huì)很快升級(jí)為輕量鎖
,那么維持偏向鎖
的過(guò)程就是在浪費(fèi)計(jì)算機(jī)資源。(不過(guò)因?yàn)?code>偏向鎖本身就很輕量,因此浪費(fèi)的資源并不多)
小結(jié):只有一個(gè)線程使用鎖的情況下,synchronized
使用的鎖為偏向鎖
。
如果鎖競(jìng)爭(zhēng)激烈,可以通過(guò)配置JDK禁用偏向鎖
。
2.2 輕量鎖
一把鎖不止一個(gè)線程使用,則偏向鎖
膨脹為輕量鎖
表現(xiàn):線程獲取輕量鎖
時(shí),會(huì)直接用CAS
修改對(duì)象頭里鎖的記錄,如果修改失敗,代表此時(shí)鎖存在多個(gè)線程的競(jìng)爭(zhēng),輕量鎖
將會(huì)膨脹為重量鎖
。
優(yōu)點(diǎn):在線程之間使用鎖不存在競(jìng)爭(zhēng)時(shí),一次CAS
操作就能獲取和退出鎖
缺點(diǎn):與偏向鎖
類(lèi)似
小結(jié):只要一把鎖不止一個(gè)線程獲取過(guò),偏向鎖
就會(huì)膨脹為輕量鎖
。
2.3 重量鎖
一把鎖存在多線程競(jìng)爭(zhēng),則輕量鎖
開(kāi)始自旋,自旋一定次數(shù)后仍沒(méi)獲取鎖,則膨脹為重量鎖
(存在競(jìng)爭(zhēng)時(shí),輕量鎖
雖然會(huì)先自旋,但是最終往往都會(huì)膨脹為重量鎖
)
表現(xiàn):線程獲取重量鎖
時(shí),如果獲取失?。存i已被其他線程獲?。?,則使用自適應(yīng)自旋鎖
,自旋一定次數(shù)后仍沒(méi)獲取鎖,則進(jìn)入阻塞隊(duì)列等待。
優(yōu)點(diǎn):未獲取到的鎖進(jìn)入阻塞隊(duì)列,節(jié)約CPU資源。(好吧感覺(jué)其實(shí)是沒(méi)有啥優(yōu)點(diǎn))
缺點(diǎn):重量鎖
是通過(guò)對(duì)象內(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)的切換,切換成本非常高。
小結(jié):只要一把鎖存在多線程競(jìng)爭(zhēng),輕量鎖
就會(huì)膨脹為重量鎖
。
自旋鎖
synchronized
的輕量鎖
,重量鎖
,使用了自適應(yīng)自旋鎖
進(jìn)行性能優(yōu)化
首先介紹自旋鎖
表現(xiàn):線程獲取鎖失敗后,不會(huì)進(jìn)入阻塞等待,而是再次嘗試去獲取鎖,如此反復(fù),直到獲取到鎖,或者自旋結(jié)束那么會(huì)阻塞等待。
解決問(wèn)題:在某些場(chǎng)景下,線程持有鎖的時(shí)間非常短。在線程獲取鎖失敗后,如果線程進(jìn)入阻塞將會(huì)帶來(lái)線程上下文的切換,上下文切換的時(shí)間可能反而高于線程反復(fù)嘗試獲取鎖的時(shí)間。
此時(shí)線程原地等待去重復(fù)獲取鎖。反而在性能上更有優(yōu)勢(shì)。
缺點(diǎn):
單核CPU沒(méi)有線程并行,反復(fù)嘗試會(huì)導(dǎo)致進(jìn)程無(wú)法繼續(xù)運(yùn)行。重復(fù)嘗試導(dǎo)致了CPU的占用,如果CPU資源緊張的話反而會(huì)性能下降如果鎖的競(jìng)爭(zhēng)時(shí)間過(guò)長(zhǎng),不僅沒(méi)有性能提升,還浪費(fèi)了大量CPU資源。
優(yōu)化:使用自適應(yīng)自旋鎖
。自適應(yīng)自旋鎖會(huì)根據(jù)之前的鎖獲取記錄,優(yōu)化調(diào)整自旋時(shí)間,避免造成不必要的自旋。
三 具體synchronized流程
總結(jié)
以上所述是小編給大家介紹的Java中synchronized關(guān)鍵字引出的多種鎖 問(wèn)題 ,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
- Java的Synchronized關(guān)鍵字學(xué)習(xí)指南(全面 & 詳細(xì))
- 深入分析JAVA Synchronized關(guān)鍵字
- Java synchronized關(guān)鍵字使用方式及特性解析
- Java synchronized關(guān)鍵字和Lock接口實(shí)現(xiàn)原理
- Java多線程并發(fā)編程 Synchronized關(guān)鍵字
- 深入講解java線程與synchronized關(guān)鍵字
- 實(shí)例解析Java中的synchronized關(guān)鍵字與線程安全問(wèn)題
- Java中synchronized關(guān)鍵字修飾方法同步的用法詳解
- Java多線程編程中synchronized關(guān)鍵字的基礎(chǔ)用法講解
- 詳解Java中synchronized關(guān)鍵字的死鎖和內(nèi)存占用問(wèn)題
- 舉例講解Java中synchronized關(guān)鍵字的用法
- Java多線程之synchronized關(guān)鍵字的使用
相關(guān)文章
java使用socket實(shí)現(xiàn)一個(gè)多線程web服務(wù)器的方法
今天小編就為大家分享一篇java使用socket實(shí)現(xiàn)一個(gè)多線程web服務(wù)器的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-10-10SpringBoot+Mybatis-Plus實(shí)現(xiàn)mysql讀寫(xiě)分離方案的示例代碼
這篇文章主要介紹了SpringBoot+Mybatis-Plus實(shí)現(xiàn)mysql讀寫(xiě)分離方案的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03Java運(yùn)行時(shí)jar終端輸出的中文日志亂碼兩種解決方式
jar包啟動(dòng),今天java開(kāi)發(fā)過(guò)來(lái)找,說(shuō)jar包啟動(dòng)日志是亂碼,這篇文章主要給大家介紹了關(guān)于Java運(yùn)行時(shí)jar終端輸出的中文日志亂碼的兩種解決方式,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01java IO流將一個(gè)文件拆分為多個(gè)子文件代碼示例
這篇文章主要介紹了java IO流將一個(gè)文件拆分為多個(gè)子文件代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12java利用遞歸算法實(shí)現(xiàn)對(duì)文件夾的刪除功能
這篇文章主要介紹了java利用遞歸算法實(shí)現(xiàn)對(duì)文件夾的刪除功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09堆排序?qū)嵗?Java數(shù)組實(shí)現(xiàn))
下面小編就為大家分享一篇使用Java數(shù)組實(shí)現(xiàn)堆排序的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12