成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

09.什么是synchronized的重量級鎖?-創(chuàng)新互聯(lián)

大家好,我是王有志。關(guān)注王有志,一起聊技術(shù),聊游戲,從北漂生活談到國際風云。

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、集團企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了增城免費建站歡迎大家使用!

鴿了這么久是給自己找到了冠冕堂皇的理由–羊了。說實話發(fā)燒那幾天真的很難受,根本不想下床,完成日常工作都已經(jīng)用盡了全部的力氣,根本沒經(jīng)歷寫文章。

言歸正傳,今天我們繼續(xù)學習synchronized的升級過程,目前只剩下最后一步了:輕量級鎖->重量級鎖。

通過今天的內(nèi)容,希望能幫助大家解答synchronized都問啥?中除鎖粗化,鎖消除以及Java 8對synchronized的優(yōu)化外全部的問題。

獲取重量級鎖

從源碼揭秘偏向鎖的升級 最后,看到synchronizer#slow_enter如果存在競爭,會調(diào)用ObjectSynchronizer::inflate方法,進行輕量級鎖的升級(膨脹)。

Tips:

void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
	......
	ObjectSynchronizer::inflate(THREAD, obj(), inflate_cause_monitor_enter)->enter(THREAD);
}

通過ObjectSynchronizer::inflate獲取重量級鎖ObjectMonitor,然后執(zhí)行ObjectMonitor::enter方法。

Tips:

  • 關(guān)于線程你必須知道的8個問題(中)中提到過該方法;
  • 問題是鎖升級(膨脹),但重點不在ObjectSynchronizer::inflate,因此代碼分析放在重量級鎖源碼分析中。
鎖的結(jié)構(gòu)

了解ObjectMonitor::enter的邏輯前,先來看ObjectMonitor的結(jié)構(gòu):

class ObjectMonitor {
	private:
		// 保存與ObjectMonitor關(guān)聯(lián)Object的markOop
		volatile markOop   _header;

		// 與ObjectMonitor關(guān)聯(lián)的Object
		void*     volatile _object;
	protected:

		// ObjectMonitor的擁有者
		void *  volatile _owner;
		
		// 遞歸計數(shù)
		volatile intptr_t  _recursions;

		// 等待線程隊列,cxq移入/Object.notify喚醒的線程
		ObjectWaiter * volatile _EntryList;
	private:

		// 競爭隊列
		ObjectWaiter * volatile _cxq;
		
		// ObjectMonitor的維護線程
		Thread * volatile _Responsible;
	protected:
	
		// 線程掛起隊列(調(diào)用Object.wait)
		ObjectWaiter * volatile _WaitSet;
}

_header字段存儲了Object的markOop,為什么要這樣?因為鎖升級后沒有空間存儲Object的markOop了,存儲到_header中是為了在退出時能夠恢復(fù)到加鎖前的狀態(tài)。
在這里插入圖片描述

Tips:

  • 實際上basicLock也存儲了對象的markOop;
  • EntryList中等待線程來自于cxq移入,或Object.notify喚醒但未執(zhí)行。
重入的實現(xiàn)

objectMonito#enter方法可以拆成三個部分,首先是競爭成功或重入的場景:

// 獲取當前線程Self
Thread * const Self = THREAD;

// CAS搶占鎖,如果失敗則返回_owner
void * cur = Atomic::cmpxchg(Self, &_owner, (void*)NULL);
if (cur == NULL) {
	// CAS搶占鎖成功直接返回
	return;
}

// CAS失敗場景
// 重量級鎖重入
if (cur == Self) {
	// 遞歸計數(shù)+1
	_recursions++;
	return;
}

// 當前線程是否曾持有輕量級鎖
// 可以看做是特殊的重入
if (Self->is_lock_owned ((address)cur)) {
	// 遞歸計數(shù)器置為1
	_recursions = 1;
	_owner = Self;
	return;
}

重入和升級的場景中,都會操作_recursions。_recursions記錄了進入ObjectMonitor的次數(shù),解鎖時要經(jīng)歷相應(yīng)次數(shù)的退出操作才能完成解鎖。

適應(yīng)性自旋

以上都是成功獲取鎖的場景,那么產(chǎn)生競爭導(dǎo)致失敗的場景是怎樣的呢?來看適應(yīng)性自旋的部分,ObjectMonitor倒數(shù)第二次對“輕量”的追求:

// 嘗試自旋來競爭鎖
Self->_Stalled = intptr_t(this);
if (Knob_SpinEarly && TrySpin (Self) >0) {
	Self->_Stalled = 0;
	return;
}

objectMonitor#TrySpin方法是對適應(yīng)性自旋的支持。Java 1.6后加入,移除默認次數(shù)的自旋,將自旋次數(shù)的決定權(quán)交給JVM。

JVM根據(jù)鎖上一次自旋情況決定,如果剛剛自旋成功,并且持有鎖的線程正在執(zhí)行,JVM會允許再次嘗試自旋。如果該鎖的自旋經(jīng)常失敗,那么JVM會直接跳過自旋過程。

Tips:

  • 適應(yīng)性自旋的原碼分析放在了重量級鎖源碼分析中;
  • objectMonitor#TryLock非常簡單,關(guān)鍵技術(shù)依舊是CAS。
互斥的實現(xiàn)

到目前為止,無論是CAS還是自旋,都是偏向鎖和輕量級鎖中出現(xiàn)過的技術(shù),為什么會讓ObjectMonitor背上“重量級”的名聲呢?

最后是競爭失敗的場景:

// 此處省略了修改當前線程狀態(tài)的代碼
for (;;) {
	EnterI(THREAD);
}

實際上,進入ObjectMonitor#EnterI后也是先嘗試“輕量級”的加鎖方式:

void ObjectMonitor::EnterI(TRAPS) {
	if (TryLock (Self) >0) {
		return;
	}

	if (TrySpin (Self) >0) {
		return;
	}
}

接來下是重量級的真正實現(xiàn):

// 將當前線程(Self)封裝為ObjectWaiter的node
ObjectWaiter node(Self);
Self->_ParkEvent->reset();
node._prev   = (ObjectWaiter *) 0xBAD;
node.TState  = ObjectWaiter::TS_CXQ;

// 將node插入到cxq的頭部
ObjectWaiter * nxt;
for (;;) {
	node._next = nxt = _cxq;
	if (Atomic::cmpxchg(&node, &_cxq, nxt) == nxt)
		break;

	// 為了減少插入到cxq頭部的次數(shù),試試能否直接獲取到鎖
	if (TryLock (Self) >0) {
		return;
	}
}

邏輯一目了然,封裝ObjectWaiter對象,并加入到cxq隊列頭部。接著往下執(zhí)行:

// 將當前線程(Self)設(shè)置為當前ObjectMonitor的維護線程(_Responsible)
// SyncFlags的默認值為0,可以通過-XX:SyncFlags設(shè)置
if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) {
	Atomic::replace_if_null(Self, &_Responsible);
}

for (;;) {
	// 嘗試設(shè)置_Responsible
	if ((SyncFlags & 2) && _Responsible == NULL) {
		Atomic::replace_if_null(Self, &_Responsible);
	}
	// park當前線程
	if (_Responsible == Self || (SyncFlags & 1)) {
		Self->_ParkEvent->park((jlong) recheckInterval);	
		// 簡單的退避算法,recheckInterval從1ms開始
		recheckInterval *= 8;
		if (recheckInterval >MAX_RECHECK_INTERVAL) {
			recheckInterval = MAX_RECHECK_INTERVAL;
		}
	} else {
		Self->_ParkEvent->park();
	}

	// 嘗試獲取鎖
	if (TryLock(Self) >0)
		break;
	if ((Knob_SpinAfterFutile & 1) && TrySpin(Self) >0)  
	    break;

	if (_succ == Self)
		_succ = NULL;
}

邏輯也不復(fù)雜,不斷的park當前線程,被喚醒后嘗試獲取鎖。需要關(guān)注-XX:SyncFlags的設(shè)置:

  • SyncFlags == 0時,synchronized直接掛起線程;
  • SyncFlags == 1時,synchronized將線程掛起指定時間。

前者是永久掛起,需要被其它線程喚醒,而后者掛起指定的時間后自動喚醒。

Tips:關(guān)于線程你必須知道的8個問題(中)聊到過parkparkEvent,底層是通過pthread_cond_waitpthread_cond_timedwait實現(xiàn)的。

釋放重量級鎖

釋放重量級鎖的源碼和注釋非常長,我們省略大部分內(nèi)容,只看關(guān)鍵部分。

重入鎖退出

我們知道,重入是不斷增加_recursions的計數(shù),那么退出重入的場景就非常簡單了:

void ObjectMonitor::exit(bool not_suspended, TRAPS) {
	Thread * const Self = THREAD;

	// 第二次持有鎖時,_recursions == 1
	// 重入場景只需要退出重入即可
	if (_recursions != 0) {
		_recursions--;
		return;
	}
	.....
}

不斷的減少_recursions的計數(shù)。

釋放和寫入

JVM的實現(xiàn)中,當前線程是鎖的持有者且沒有重入時,首先會釋放自己持有的鎖,接著將改動寫入到內(nèi)存中,最后還肩負著喚醒下一個線程的責任。先來看釋放和寫入內(nèi)存的邏輯:

// 置空鎖的持有者
OrderAccess::release_store(&_owner, (void*)NULL);

// storeload屏障,
OrderAccess::storeload();

// 沒有競爭線程則直接退出
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
	TEVENT(Inflated exit - simple egress);
	return;
}

storeload屏障,對于如下語句:

store1;
storeLoad;
load2

保證store1指令的寫入在load2指令執(zhí)行前,對所有處理器可見。

Tips:volatile中詳細解釋內(nèi)存屏障。

喚醒的策略

執(zhí)行釋放鎖和寫入內(nèi)存后,只需要喚醒下一個線程來“交接”鎖的使用權(quán)。但是有兩個“等待隊列”:cxqEntryList,該從哪個開始喚醒呢?

Java 11前,根據(jù)QMode來選擇不同的策略:

  • QMode == 0,默認策略,將cxq放入EntryList
  • QMode == 1,翻轉(zhuǎn)cxq,并放入EntryList;
  • QMode == 2,直接從cxq中喚醒;
  • QMode == 3,將cxq移入到EntryList的尾部;
  • QMode == 4,將cxq移入到EntryList的頭部。

不同的策略導(dǎo)致了不同的喚醒順序,現(xiàn)在你知道為什么說synchronized是非公平鎖了吧?

objectMonitor#ExitEpilog方法就很簡單了,調(diào)用的是與park對應(yīng)的unpark方法,這里就不多說了。

Tips:Java 12的objectMonitor移除了QMode,也就是說只有一種喚醒策略了。

總結(jié)

我們對重量級鎖做個總結(jié)。synchronized的重量級鎖是ObjectMonitor,它使用到的關(guān)鍵技術(shù)有CAS和park。相較于mutex#Monitor來說,它們的本質(zhì)相同,對park的封裝,但ObjectMonitor是做了大量優(yōu)化的復(fù)雜實現(xiàn)。

我們看到了重量級鎖是如何實現(xiàn)重入性的,以及喚醒策略導(dǎo)致的“不公平”。那么我們常說的synchronized保證了原子性,有序性和可見性,是如何實現(xiàn)的呢?

大家可以先思考下這個問題,下篇文章會做一個全方位的總結(jié),給synchronized收下尾。


好了,今天就到這里了,Bye~~

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

本文題目:09.什么是synchronized的重量級鎖?-創(chuàng)新互聯(lián)
瀏覽路徑:http://jinyejixie.com/article48/dehohp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營銷、動態(tài)網(wǎng)站、軟件開發(fā)ChatGPT、網(wǎng)頁設(shè)計公司、定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計
麟游县| 乐业县| 荔波县| 梅州市| 黔江区| 梅州市| 辽宁省| 出国| 凌海市| 山丹县| 婺源县| 行唐县| 图木舒克市| 曲阳县| 洱源县| 宁河县| 海阳市| 资阳市| 克山县| 蒙山县| 长白| 南汇区| 丽水市| 项城市| 芷江| 门头沟区| 民县| 怀集县| 佛冈县| 武汉市| 中方县| 阿鲁科尔沁旗| 且末县| 昌都县| 绿春县| 固始县| 漠河县| 榆中县| 安福县| 富平县| 龙南县|