自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

再有人說synchronized是重量級鎖,就把這篇文章扔給他看

開發(fā) 前端
synchronized鎖經過多次迭代優(yōu)化,已經不像以前那么重了,在JDK1.8的ConcurrentHashMap源碼中已經大量使用synchronized做同步控制,大家在日常開發(fā)中可以放心使用了。

?synchronized作為Java程序員最常用同步工具,很多人卻對它的用法和實現(xiàn)原理一知半解,以至于還有不少人認為synchronized是重量級鎖,性能較差,盡量少用。

但不可否認的是synchronized依然是并發(fā)首選工具,連volatile、CAS、ReentrantLock都無法動搖synchronized的地位。synchronized是工作面試中的必備技能,今天就跟著一燈一塊深入剖析synchronized底層到底做了哪些優(yōu)化?

synchronized是用來加鎖的,而鎖是加在對象上面,所以需要先聊一下JVM中對象構成。

1. 對象的構成

Java對象在JVM內存中由三塊區(qū)域組成:對象頭、實例數(shù)據和對齊填充。

對象頭又分為:Mark Word(標記字段)、Class Pointer(類型指針)、數(shù)組長度(如果是數(shù)組)。

實例數(shù)據是對象實際有效信息,包括本類信息和父類信息等。

對齊填充沒有特殊含義,由于虛擬機要求 對象起始地址必須是8字節(jié)的整數(shù)倍,作用僅是字節(jié)對齊。

Class Pointer是對象指向它的類元數(shù)據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。

重點關注一下對象頭中Mark Word,里面存儲了對象的hashcode、鎖狀態(tài)標識、持有鎖的線程id、GC分代年齡等。

在32為的虛擬機中,Mark Word的組成如下:

2. synchronized鎖優(yōu)化

從JDK1.6開始,就對synchronized的實現(xiàn)機制進行了較大調整,包括使用JDK1.5引進的CAS自旋之外,還增加了自適應的CAS自旋、鎖消除、鎖粗化、偏向鎖、輕量級鎖等優(yōu)化策略。由于使得synchronized性能極大提高,同時語義清晰、操作簡單、無需手動關閉,所以推薦在允許的情況下盡量使用此關鍵字,同時在性能上此關鍵字還有優(yōu)化的空間。

鎖主要存在四種狀態(tài),依次是:無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級鎖狀態(tài)、重量級鎖狀態(tài),性能依次是從高到低。鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖。但是鎖的升級是單向的,也就是說只能從低到高升級,不會出現(xiàn)鎖的降級。

在 JDK 1.6 中默認是開啟偏向鎖和輕量級鎖的,可以通過-XX:-UseBiasedLocking來禁用偏向鎖。

2.1 自旋鎖

線程的掛起與恢復需要CPU從用戶態(tài)轉為內核態(tài),頻繁的阻塞和喚醒對CPU來說是一件負擔很重的工作,勢必會給系統(tǒng)的并發(fā)性能帶來很大的壓力。同時我們發(fā)現(xiàn)在許多應用上面,對象鎖的鎖狀態(tài)只會持續(xù)很短一段時間,為了這一段很短的時間頻繁地阻塞和喚醒線程是非常不值得的。

自旋鎖就是指當一個線程嘗試獲取某個鎖時,如果該鎖已被其他線程占用,就一直循環(huán)檢測鎖是否被釋放,而不是進入線程掛起或睡眠狀態(tài)。

自旋鎖適用于鎖保護的臨界區(qū)很小的情況,臨界區(qū)很小的話,鎖占用的時間就很短。自旋等待不能替代阻塞,雖然它可以避免線程切換帶來的開銷,但是它占用了CPU處理器的時間。如果持有鎖的線程很快就釋放了鎖,那么自旋的效率就非常好,反之,自旋的線程就會白白消耗掉處理的資源,它不會做任何有意義的工作,這樣反而會帶來性能上的浪費。所以說,自旋等待的時間(自旋的次數(shù))必須要有一個限度,如果自旋超過了定義的時間仍然沒有獲取到鎖,則應該被掛起。

自旋鎖在JDK 1.4.2中引入,默認關閉,但是可以使用-XX:+UseSpinning開開啟,在JDK1.6中默認開啟。同時自旋的默認次數(shù)為10次,可以通過參數(shù)-XX:PreBlockSpin來調整。

2.2 自適應自旋鎖

JDK 1.6引入了更加智能的自旋鎖,即自適應自旋鎖。自適應就意味著自旋的次數(shù)不再是固定的,它是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定。那它如何進行適應性自旋呢?

線程如果自旋成功了,那么下次自旋的次數(shù)會更加多,因為虛擬機認為既然上次成功了,那么此次自旋也很有可能會再次成功,那么它就會允許自旋等待持續(xù)的次數(shù)更多。反之,如果對于某個鎖,很少有自旋能夠成功,那么在以后要或者這個鎖的時候自旋的次數(shù)會減少甚至省略掉自旋過程,以免浪費CPU資源。

有了自適應自旋鎖,隨著程序運行和性能監(jiān)控信息的不斷完善,虛擬機對程序鎖的狀況預測會越來越準確,虛擬機會變得越來越聰明。

2.3 鎖消除

JVM在JIT編譯時通過對運行上下文的掃描,經過逃逸分析,對于某段代碼不存在競爭或共享的可能性,就會講這段代碼的鎖消除,提升程序運行效率。

public void method(){
final Object LOCK = new Object();
synchronized (LOCK) {
// do something
}
}

比如上面代碼中鎖,是方法中私有的,又是不可變的,完全沒必要加鎖,所以JVM就會執(zhí)行鎖消除。

2.4 鎖粗化

按理來說,同步塊的作用范圍應該盡可能小,僅在共享數(shù)據的實際作用域中才進行同步,這樣做的目的是為了使需要同步的操作數(shù)量盡可能縮小,縮短阻塞時間,如果存在鎖競爭,那么等待鎖的線程也能盡快拿到鎖。
但是加鎖解鎖也需要消耗資源,如果存在一系列的連續(xù)加鎖解鎖操作,可能會導致不必要的性能損耗。

鎖粗化就是將多個連續(xù)的加鎖、解鎖操作連接在一起,擴展成一個范圍更大的鎖,避免頻繁地加鎖解鎖操作。

public void method(Object LOCK){
synchronized (LOCK) {
// do something1
}
synchronized (LOCK) {
// do something2
}
}

比如上面方法中兩個加鎖的代碼塊,完全可以合并成一個,減少頻繁加鎖解鎖帶來的開銷,提升程序運行效率。

2.5 偏向鎖

為什么要引入偏向鎖?

因為經過HotSpot的作者大量的研究發(fā)現(xiàn),大多數(shù)時候是不存在鎖競爭的,通常是一個線程多次獲得同一把鎖,因此如果每次都要競爭鎖會增大很多沒有必要付出的代價,為了降低獲取鎖的代價,才引入的偏向鎖。

2.6 輕量級鎖

輕量級鎖考慮的是競爭鎖對象的線程不多,而且線程持有鎖的時間也不長的場景。因為阻塞線程需要CPU從用戶態(tài)轉到內核態(tài),代價較大,如果剛剛阻塞不久這個鎖就被釋放了,那這個代價就有點得不償失了,因此這個時候就干脆不阻塞這個線程,讓它自旋(CAS)這等待鎖釋放。

加鎖過程:當代碼進入同步塊時,如果同步對象為無鎖狀態(tài)時,當前線程會在棧幀中創(chuàng)建一個鎖記錄(Lock Record)區(qū)域,同時將鎖對象的對象頭中 Mark Word 拷貝到鎖記錄中,再嘗試使用 CAS 將 Mark Word 更新為指向鎖記錄的指針。如果更新成功,當前線程就獲得了鎖。

解鎖過程:輕量鎖的解鎖過程也是利用 CAS 來實現(xiàn)的,會嘗試鎖記錄替換回鎖對象的 Mark Word 。如果替換成功則說明整個同步操作完成,失敗則說明有其他線程嘗試獲取鎖,這時就會喚醒被掛起的線程(此時已經膨脹為重量鎖)

2.7 重量級鎖

synchronized是通過對象內部的監(jiān)視器鎖(Monitor)來實現(xiàn)的。但是監(jiān)視器鎖本質又是依賴于底層的操作系統(tǒng)的互斥鎖(Mutex Lock)來實現(xiàn)的。

重量級鎖的工作流程:當系統(tǒng)檢查到鎖是重量級鎖之后,會把等待想要獲得鎖的線程進行阻塞,被阻塞的線程不會消耗cpu。但是阻塞或者喚醒一個線程時,都需要操作系統(tǒng)來幫忙,這就需要從用戶態(tài)轉換到內核態(tài),而轉換狀態(tài)是需要消耗很多時間的,有可能比用戶執(zhí)行代碼的時間還要長,所以重量級鎖的開銷還是很大的。

在鎖競爭激烈、鎖持有時間長的場景,還是適合使用重量級鎖的。

2.8 鎖升級過程

2.9 鎖的優(yōu)缺點對比

鎖的性能從低到高,依次是無鎖、偏向鎖、輕量級鎖、重量級鎖。不同的鎖只是適合不同的場景,大家可以依據實際場景自行選擇。

3. 總結

synchronized鎖經過多次迭代優(yōu)化,已經不像以前那么重了,在JDK1.8的ConcurrentHashMap源碼中已經大量使用synchronized做同步控制,大家在日常開發(fā)中可以放心使用了。


責任編輯:武曉燕 來源: 今日頭條
相關推薦

2018-08-07 16:01:32

synchronizevolatilefinal

2022-09-26 10:09:08

MVCC控制并發(fā)

2021-02-22 13:32:19

MySQLSQL索引

2018-12-07 09:31:52

分布式鎖服務框架分布式系統(tǒng)

2022-11-08 09:33:36

訂單系統(tǒng)電商

2018-07-03 14:54:25

Java內存模型

2020-12-11 08:23:06

JavaMemory Mode內存模型

2021-05-31 09:42:48

MySQL隔離級別

2021-03-08 10:25:37

MySQL數(shù)據庫索引

2021-03-08 12:47:42

MySQL查詢數(shù)據

2022-05-23 09:41:27

分庫分表數(shù)據庫算法

2024-01-11 08:12:20

重量級監(jiān)視器

2024-08-13 14:08:25

2020-04-20 13:11:21

HashMap底層存儲

2023-12-11 08:32:58

數(shù)據庫DruidDBA

2019-11-04 15:00:50

MySQL索引B+樹

2019-11-05 14:06:07

MySQLB+索引

2019-09-19 14:03:32

B樹節(jié)點數(shù)據結構

2024-03-18 12:21:28

Java輕量級鎖重量級鎖

2020-04-28 09:15:58

HashMapJava數(shù)組
點贊
收藏

51CTO技術棧公眾號