java并发之synchronized实现原理及其优化
1:synchronnized概述
synchronized修飾的方法或代碼塊相當(dāng)于并發(fā)中的臨界區(qū),即在同一時刻jvm只允許一個線程進(jìn)入執(zhí)行。synchronized是通過鎖機(jī)制實現(xiàn)同一時刻只允許一個線程來訪問共享資源的。另外synchronized鎖機(jī)制還可以保證線程并發(fā)運(yùn)行的原子性,有序性,可見性。
2:synchronized的實現(xiàn)原理
Monitor被翻譯為監(jiān)視器或管程
每個Java對象都可以關(guān)聯(lián)一個Monitor對象,如果使用synchronized給對象上鎖(重量級)之后,該對象頭的Mark Word中就被設(shè)置指向Monitor對象的指針
剛開始Monitor中Owner為null當(dāng)Thread-2執(zhí)行synchronized(obj)就會將Monitor的所有者Owner置為Thread-2,Monitor中只能有一個Owner
在Thread-2上鎖的過程中,如果Thread-3,Thread-4,Thread-5也來執(zhí)行synchronized(obj),就會進(jìn)入EntryList BLOCKED
Thread-2執(zhí)行完同步代碼塊的內(nèi)容,然后喚醒EntryList中等待的線程來競爭鎖,競爭的時是非公平的
注意
synchronized必須是進(jìn)入同一個對象的monitor才有上述的效果
不加synchronized的對象不會關(guān)聯(lián)監(jiān)視器,不遵從以上規(guī)則
3:為什么要對synchronized進(jìn)行優(yōu)化
Synchronized是通過對象內(nèi)部的一個叫做監(jiān)視器鎖(monitor)來實現(xiàn)的。但是監(jiān)視器鎖本質(zhì)又是依賴于底層的操作系統(tǒng)的Mutex Lock來實現(xiàn)的。而操作系統(tǒng)實現(xiàn)線程之間的切換就需要從用戶態(tài)轉(zhuǎn)換到核心態(tài),這個成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對比較長的時間,這就是為什么Synchronized效率低的原因。因此,這種依賴于操作系統(tǒng)Mutex Lock所實現(xiàn)的鎖我們稱之為“重量級鎖”。
在jdk1.5之前,只有synchronized重量級鎖,實現(xiàn)需要借助操作系統(tǒng),是比較消耗性能的操作,在1.6之中為了提高性能,便對synchronized鎖進(jìn)行了優(yōu)化,實現(xiàn)了各種鎖優(yōu)化技術(shù),如:適應(yīng)性自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖。
4:輕量級鎖
(1):是什么
輕量級鎖是jdk1.6中加入的新型鎖機(jī)制,它名字中的“輕量級”是相對于使用操作系統(tǒng)互斥量來實現(xiàn)的重量級鎖而言的。首先需要強(qiáng)調(diào)的是,輕量級鎖并不是來代替重量級鎖的,它的本意是在沒有多線程競爭的前提下,減少傳統(tǒng)重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。在解釋輕量級鎖的執(zhí)行過程之前,先明白一點,輕量級鎖所適應(yīng)的場景是線程交替執(zhí)行同步塊的情況,如果存在同一時間訪問同一鎖的情況,就會導(dǎo)致輕量級鎖膨脹為重量級鎖。
(2):上鎖的過程
- 在輕量級鎖的執(zhí)行過程上,在代碼進(jìn)入同步塊的時候,如果此同步對象沒有被鎖定,也就是說此時對象的鎖標(biāo)記位為“01”狀態(tài),那么虛擬機(jī)首先將在線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,內(nèi)部可以儲存鎖定對象的MarkWord。
-
然后虛擬機(jī)將使用CAS操作嘗試將棧幀中的Lock Record更新為指向該對象的Mark Word的指針,如果這個更新動作成功了,那么這個線程就擁有了該對象的鎖,并且對象Mark Word的鎖標(biāo)志位將轉(zhuǎn)變位00,即表示此對象處于輕量級鎖定狀態(tài),
-
如果cas操作更新失敗的話
-
對比重量級別的鎖:
如果線程之間不存在鎖的競爭,與重量級鎖相比,輕量級鎖避免使用了互斥信號量,只使用了簡單的CAS操作,但如果存在鎖競爭,輕量級鎖除了使用互斥信號量,還要額外發(fā)生CAS操作,因此在有競爭的情況下,輕量級鎖會比重量級鎖開銷更大。
5:鎖膨脹
(1):是什么
如果在嘗試加輕量級鎖的過程中,CAS操作無法成功,這時一種情況就是有其它線程為此對象加上了輕量級鎖(有競爭),這時需要進(jìn)行鎖膨脹,將輕量級鎖變?yōu)橹亓考夋i。
(2):如何形成的
那么當(dāng)Thread-0去使用CAS去解鎖的時候,就不能按原先的步驟進(jìn)行了,因為現(xiàn)在的鎖為重量級鎖
6:自旋鎖
自旋鎖原理非常簡單,如果持有鎖的線程能在很短時間內(nèi)釋放鎖資源,那么那些等待競爭鎖的線程就不需要做內(nèi)核態(tài)和用戶態(tài)之間的切換進(jìn)入阻塞掛起狀態(tài),它們只需要等一等(自旋),等持有鎖的線程釋放鎖后即可立即獲取鎖,這樣就避免用戶線程和內(nèi)核的切換的消耗。但是線程自旋是需要消耗cpu的,說白了就是讓cpu在做無用功,如果一直獲取不到鎖,那線程也不能一直占用cpu自旋做無用功,所以需要設(shè)定一個自旋等待的最大時間。
如果持有鎖的線程執(zhí)行的時間超過自旋等待的最大時間仍沒有釋放鎖,這時其他爭用線程會停止自旋進(jìn)入阻塞狀態(tài)。
7:偏向鎖
(1):是什么
Java偏向鎖是在jdk1.6中引入的,它的目的是消除數(shù)據(jù)無競爭情況下的同步原語,進(jìn)一步提高程序的運(yùn)行性能。如果說輕量級鎖是在無競爭的情況下使用CAS操作去消除同步使用的互斥量,那偏向鎖就是在無競爭的情況下把整個同步都消除連CAS都不做。
偏向鎖,顧名思義,它會偏向于第一個訪問它的線程,如果在運(yùn)行過程中,同步鎖只有一個線程訪問,不存在多線程爭用鎖的情況,則持有偏向鎖的線程將永遠(yuǎn)是不需要在進(jìn)行同步。
如果運(yùn)行過程中,遇到其它線程搶占資源,則持有偏向鎖的線程會被掛起,jvm會消除它身上的偏向鎖,將鎖恢復(fù)到標(biāo)準(zhǔn)的輕量級鎖。
(2):為什么不直接使用輕量級鎖
引入偏向級鎖是為了減少在無多線程競爭的情況下,盡量減少不必要的輕量級鎖執(zhí)行路徑。
因為輕量級鎖的獲取及釋放依賴多次CAS原子指令,而偏向鎖只需要在置換線程時,使用CAS操作把獲取到這個鎖的線程線程的ID記錄在對象的Mark Word之中,從而減少性能消耗,不過遇到多線程競爭的情況時就必須撤銷偏向鎖。
另外一個原因就是,在虛擬機(jī)中,大多時候是不存在鎖競爭的,常常是一個線程多次獲取同一個鎖,因此直接使用輕量級鎖會增加很多不必要的消耗,所以可以才引入了偏向鎖。
(3):偏向鎖的使用背景
偏向鎖可以提高帶有同步但無競爭的程序性能。但是如果程序中大多數(shù)鎖總是被多個不同的線程訪問,那么偏向鎖模式就是多余的。
7:總結(jié)
總結(jié)
以上是生活随笔為你收集整理的java并发之synchronized实现原理及其优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 养心菜的功效与作用、禁忌和食用方法
- 下一篇: 蓝桥杯- 包子凑数