欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

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

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

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

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

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

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

獲取重量級(jí)鎖

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

Tips:

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

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

Tips:

  • 關(guān)于線程你必須知道的8個(gè)問題(中)中提到過該方法;
  • 問題是鎖升級(jí)(膨脹),但重點(diǎn)不在ObjectSynchronizer::inflate,因此代碼分析放在重量級(jí)鎖源碼分析中。
鎖的結(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;
		
		// 遞歸計(jì)數(shù)
		volatile intptr_t  _recursions;

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

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

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

Tips:

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

objectMonito#enter方法可以拆成三個(gè)部分,首先是競(jìng)爭(zhēng)成功或重入的場(chǎng)景:

// 獲取當(dāng)前線程Self
Thread * const Self = THREAD;

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

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

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

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

適應(yīng)性自旋

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

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

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

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

Tips:

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

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

最后是競(jìng)爭(zhēng)失敗的場(chǎng)景:

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

實(shí)際上,進(jìn)入ObjectMonitor#EnterI后也是先嘗試“輕量級(jí)”的加鎖方式:

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

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

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

// 將當(dāng)前線程(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對(duì)象,并加入到cxq隊(duì)列頭部。接著往下執(zhí)行:

// 將當(dāng)前線程(Self)設(shè)置為當(dāng)前ObjectMonitor的維護(hù)線程(_Responsible)
// SyncFlags的默認(rèn)值為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當(dāng)前線程
	if (_Responsible == Self || (SyncFlags & 1)) {
		Self->_ParkEvent->park((jlong) recheckInterval);	
		// 簡(jiǎn)單的退避算法,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當(dāng)前線程,被喚醒后嘗試獲取鎖。需要關(guān)注-XX:SyncFlags的設(shè)置:

  • 當(dāng)SyncFlags == 0時(shí),synchronized直接掛起線程;
  • 當(dāng)SyncFlags == 1時(shí),synchronized將線程掛起指定時(shí)間。

前者是永久掛起,需要被其它線程喚醒,而后者掛起指定的時(shí)間后自動(dòng)喚醒。

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

釋放重量級(jí)鎖

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

重入鎖退出

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

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

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

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

釋放和寫入

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

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

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

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

storeload屏障,對(duì)于如下語句:

store1;
storeLoad;
load2

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

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

喚醒的策略

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

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

  • QMode == 0,默認(rèn)策略,將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方法就很簡(jiǎn)單了,調(diào)用的是與park對(duì)應(yīng)的unpark方法,這里就不多說了。

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

總結(jié)

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

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

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


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

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

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

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

廣告

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

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)
最近日韩在线免费黄片| 热久久这里只有精品视频| 一区二区三区人妻在线| 极品少妇一区二区三区精品视频| 欧美日韩国产另类一区二区| 老司机精品一区二区三区| 久久人人爽人人爽大片av| 色婷婷国产熟妇人妻露脸| 中文字幕中文字幕在线十八区 | 国产精品欧美在线观看| 日本加勒比在线观看不卡| 亚洲欧美国产中文色妇| 欧美日韩精品综合一区| 国产精品一区二区三区黄色片| 日韩精品一区二区三区四区| 国产精品国产亚洲区久久| 国产女高清在线看免费观看| 国产白丝粉嫩av在线免费观看| 99久久国产亚洲综合精品| 久久精品国产亚洲av麻豆| 亚洲男人的天堂色偷偷| 九九热视频免费在线视频| 免费观看潮喷到高潮大叫 | 好吊妞视频免费在线观看| 久久热在线视频免费观看| 国产精品一区二区三区黄色片| 国产一区二区三区av在线| 国产亚洲精品一二三区| 国产日韩熟女中文字幕| 国产又粗又黄又爽又硬的| 老熟女露脸一二三四区| 亚洲视频偷拍福利来袭| 精品al亚洲麻豆一区| 国产精品熟女乱色一区二区| 韩国日本欧美国产三级| 日韩精品免费一区三区| 成人精品亚洲欧美日韩| 亚洲欧洲一区二区综合精品| 中文文精品字幕一区二区| 欧美午夜一级特黄大片| 日本高清不卡在线一区|