Java併發學習 4 : Synchronized實現原理

Synchronized實現原理

Java對象頭

對象是存放在堆內存之中的,大致可以分爲對象圖,實例變量,填充字節。

其中對象頭中有一個叫做MarkWord的區域是用來存儲包含鎖相關的信息:是否有鎖,鎖的類型,偏向鎖偏向的線程的ID,鎖的狀態.

64位虛擬機中的模型

這是64位虛擬機中的模型(32位主要結構與其相同,位數有所不同)

5.2 幾種鎖的區別

​ JDK1.6之前,synchronized只有一種鎖,就是當時的重量級鎖,而JDK1.6之後給synchronized增加了幾個新的鎖,比如:偏向鎖,輕量級鎖,之後纔是–>重量級鎖。有效減少了申請鎖和釋放鎖嗲來的性能消耗。

引入原因:JVM認爲:大部分時候是不存在鎖競爭的,常常是一個線程多次獲得同一個鎖,因此如果每次都要競爭鎖會增大很多沒有必要付出的代價,爲了降低獲取鎖的代價,才引入的偏向鎖。

Tip : 比如一個for循環內調用方法,大多數情況是不需要競爭的。

偏向鎖就好像一把寶刀,任何人想使用它,需要先問他:寶刀呀寶刀,你有主人嗎?

寶刀說沒有的話,則開始認主。

如果寶刀說有,那麼就需要查詢他主人是否還活着?死了的話就可以重新認主。

還活着的話,那麼其他人就不能再要求使用它,並且寶刀進入自衛模式(輕量級鎖),防止爭奪。

偏向鎖:當線程1 訪問代碼塊並獲取鎖對象時,會在java對象頭和棧幀中記錄偏向的鎖的threadID,因爲 偏向鎖不會主動釋放鎖,因此之後線程1再次獲取鎖的時候,需要 比對當前線程的threadID和Java對象頭中的threadID是否一致,如果相同,說明還是線程1獲取鎖對象,則直接放行。

​ 如果不一致,說明是非線程1想要得到資源,那麼需要詢問對象頭中的線程1是否還活着,如果依然活着,那麼立刻查找線程1的棧幀信息,如果線程1說:”我還要這個資源,“那麼暫停當前線程1,將偏向鎖升級位輕量級鎖(先撤銷偏向鎖,增加輕量級鎖).

​ 如果線程1已經死了,活着線程1不再需要這個資源的話,將對象鎖狀態設爲無鎖狀態,重新偏向新的線程。

[外鏈圖片轉存失敗(img-lRe0mi6V-1564649471748)(D:\我的堅果雲\Pictures\偏向鎖升級過程.jpg)]

輕量級鎖

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

還是剛纔那把寶刀,寶刀認爲可能剛纔那個人是無意的,所以暫時還不準備砍傷他。

如果寶刀過一會發現自己主人死了/把自己拋棄了,那麼換一個耐心等待它的主人。

但是如果爭奪的人過多,寶刀就會覺得:“好煩呀!我又不是那麼隨便的刀,我主人活得好好的,你們來找事”

“把你們全部送進醫院裏!!!”,然後進入重量級鎖狀態,誰碰刀,誰就回家療養。

​ 實現:通過CPU空轉實現自旋,比如讓當前進程進入一個while循環,跳出條件是沒有線程爭奪這個資源。

一個類似CPU空轉的例子:

public final int incrementAndGet() {
    	// 不管怎樣,該線程一定要進入到一個無限循環中
        for (;;) {	// 不斷的嘗試。
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))	// 直到目標資源是期待的樣子
                return next;		// 結束這該死的循環
        } // 如果不符合,空轉CPU。 
}

​ CAS的原理如上,如果資源是期待的模樣,那麼for循環是隱藏的,對性能沒有任何影響。少數線程競爭資源時,空轉CPU造成的資源浪費遠小於從用戶態轉到內核態的代價。

就像堵車,你不願意看到紅燈就熄火,只有堵死的時候,纔會熄火。

​ 那麼什麼時候將紅燈變成堵車呢? 輕量級鎖變爲重量級鎖呢?

A : 當等待線程自旋時間過後依然沒有得到鎖,並且又來了一個線程過來競爭這個鎖對象,那麼此時這個輕量級鎖就會膨脹爲重量級鎖。重量級鎖把除了擁有鎖的線程都阻塞,防止CPU空轉。

注意:爲了避免無用的自旋,輕量級鎖一旦膨脹爲重量級鎖就不會再降級爲輕量級鎖了;偏向鎖升級爲輕量級鎖也不能再降級爲偏向鎖。一句話就是鎖可以升級不可以降級,但是偏向鎖狀態可以被重置爲無鎖狀態。

重量級鎖:

​ 沒什麼好說的,就是標準的,誰想搶資源誰就睡,睡到直到輪到它爲止。

鎖狀態 優點 缺點 適用場景
偏向鎖 加鎖解鎖無需額外的消耗, 和非同步方法時間相差納秒級別 如果競爭線程多,那麼會帶來額外的鎖撤銷的消耗 基本沒有線程競爭鎖的同步場景
輕量級鎖 競爭的線程不會阻塞,使用自旋,提高程序響應速度。 如果始終不能獲取鎖,長時間的自旋會造成CPU消耗 適用於少量線程競爭鎖對象,且線程持有鎖的時間不長,追求相應速度的場景
重量級鎖 線程競爭不適用CPU自旋,不會導致CPU空轉消耗CPU資源 線程阻塞,相應時間長 很多線程競爭鎖,且鎖持有的時間長,追求吞吐量的場景。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章