本篇文章為大家展示了Java中Synchronized的原理是什么,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
公司主營業(yè)務(wù):網(wǎng)站設(shè)計制作、成都做網(wǎng)站、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出新絳免費做網(wǎng)站回饋大家。
Synchronized
是基于底層操作系統(tǒng)的 Mutex Lock 實現(xiàn)的,每次獲取鎖和釋放鎖的操作都會帶來用戶態(tài)
和內(nèi)核態(tài)
的切換,從而增加系統(tǒng)性能開銷。
因此,在鎖競爭激烈的情況下,Synchronized
同步鎖在性能上就表現(xiàn)得非常糟糕,它也常被大家稱為重量級鎖
。
到了 JDK1.5 版本,并發(fā)包中新增了 Lock 接口來實現(xiàn)鎖功能,它提供了與 Synchronized 關(guān)鍵字類似的同步功能,只是在使用時需要顯示獲取鎖和釋放鎖。
在單個線程重復(fù)申請鎖的情況下,JDK1.5 版本的 Lock 性能要比 Synchronized 鎖的性能好很多,也就是當(dāng)時的 Synchronized 并不具備可重入鎖
的功能。
那么當(dāng)時的 Synchronized 是怎么實現(xiàn)的?又為什么不具備可重入的功能呢?
JVM 中的同步是基于進(jìn)入和退出管程(Monitor)對象實現(xiàn)的。每個對象實例都會有一個 Monitor,Monitor 可以和對象一起創(chuàng)建、銷毀。
當(dāng)多個線程同時訪問一段同步代碼時,多個線程會先被存放在EntryList集合
(也可稱為阻塞隊列
)中,處于BLOCKED
狀態(tài)的線程,都會被加入到該列表。
接下來當(dāng)線程獲取到對象的 Monitor 時,Monitor 是依靠底層操作系統(tǒng)的 Mutex Lock 來實現(xiàn)互斥的,線程申請 Mutex 成功,則持有該 Mutex,其它線程將無法獲取到該 Mutex。
如果線程調(diào)用 wait() 方法,就會釋放當(dāng)前持有的 Mutex,并且該線程會進(jìn)入WaitSet集合
(也可稱為等待隊列
)中,等待下一次被喚醒。此時線程會處于WAITING
或者TIMEDWAITING
狀態(tài),
如果當(dāng)前線程順利執(zhí)行完方法,也將釋放 Mutex。
總的來說,就是同步鎖在這種實現(xiàn)方式中,因 Monitor 是依賴于底層的操作系統(tǒng)實現(xiàn),存在用戶態(tài)
與內(nèi)核態(tài)
之間的切換(可以理解為上下文切換
),所以增加了性能開銷。
為了提升性能,JDK1.6 引入了偏向鎖、輕量級鎖、重量級鎖概念,來減少鎖競爭帶來的上下文切換,而正是新增的Java對象頭
實現(xiàn)了鎖升級
功能。
所謂鎖升級
,就是指
>Synchronized 同步鎖初始為偏向鎖
,隨著線程競爭越來越激烈,偏向鎖
升級到輕量級鎖
,最終升級到重量級鎖
。
偏向鎖
主要用來優(yōu)化同一線程多次申請同一個鎖的競爭,也就是現(xiàn)在的Synchronized鎖
實際已經(jīng)擁有了可重入鎖的功能。
為什么要有偏向鎖
?因為在我們的應(yīng)用中,可能大部分時間是同一個線程競爭鎖資源(比如單線程操作一個線程安全的容器),如果這個線程每次都要獲取鎖和釋放鎖,那么就在不斷的從內(nèi)核態(tài)
與用戶態(tài)
之間切換。
那么有了偏向鎖
,當(dāng)一個線程再次訪問這個同步代碼或方法時,該線程只需去對象頭中去判斷一下是否當(dāng)前線程是否持有該偏向鎖就可以了。
一旦出現(xiàn)其它線程競爭鎖資源時,偏向鎖就會被撤銷。偏向鎖的撤銷需要等待全局安全點
(JVM的stop the world
),暫停持有該鎖的線程,同時檢查該線程是否還在執(zhí)行該方法,如果是,則升級鎖,反之則被其它線程搶占。
當(dāng)有另外一個線程競爭獲取這個鎖時,由于該鎖已經(jīng)是偏向鎖,當(dāng)發(fā)現(xiàn)對象頭中的線程 ID 不是自己的線程 ID,就會進(jìn)行 CAS 操作獲取鎖,如果獲取成功,直接替換對象頭中的線程 ID 為自己的 ID,該鎖會保持偏向鎖
狀態(tài);如果獲取鎖失敗,代表當(dāng)前鎖有一定的競爭,偏向鎖將升級為輕量級鎖
。
輕量級鎖
適用于線程交替執(zhí)行同步塊的場景,絕大部分的鎖在整個同步周期內(nèi)都不存在長時間的競爭。
輕量級鎖也支持自旋
,因此其他線程再次爭搶時,如果CAS
失敗,將不再會進(jìn)入阻塞狀態(tài)
,而是不斷自旋。
之所以自旋更好,是因為之前說了,默認(rèn)線程持有鎖的時間都不會太長,如果線程被掛起阻塞可能代價會更高。
如果自旋鎖重試之后搶鎖依然失敗,那么同步鎖就會升級至重量級鎖
。
在這個狀態(tài)下,未搶到鎖的線程都會進(jìn)入 Monitor,之后會被阻塞在WaitSet集合
中,也就變成了優(yōu)化之前的Synchronized鎖
。
偏向鎖
升級為輕量級鎖
時,會發(fā)生stop the world
,如果系統(tǒng)常常是多線程競爭,那么禁止偏向鎖也許是更好的選擇,可以通過以下JVM參數(shù)
進(jìn)行優(yōu)化:
// 關(guān)閉偏向鎖(默認(rèn)打開) -XX:-UseBiasedLocking // 設(shè)置重量級鎖 -XX:+UseHeavyMonitors
輕量級鎖
擁有自旋鎖
的功能,那么如果線程持有鎖的時間很長,那么競爭的線程也會常常處于自旋狀態(tài),占用系統(tǒng) CPU ,增加系統(tǒng)開銷,那么此時關(guān)閉自旋鎖的優(yōu)化可以更好一些:
-XX:-UseSpinning
上述內(nèi)容就是Java中Synchronized的原理是什么,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。