Java并發(fā)底層實(shí)現(xiàn)原理學(xué)習(xí)心得
我們知道java實(shí)現(xiàn)的并發(fā)操作最后肯定是由我們的CPU完成的,中間經(jīng)歷了將java源碼編譯成.class文件,然后進(jìn)行加載,然后虛擬機(jī)執(zhí)行引擎進(jìn)行執(zhí)行,解釋為匯編語(yǔ)言,然后轉(zhuǎn)為操作系統(tǒng)指令,然后轉(zhuǎn)為1,0,最后CPU進(jìn)行識(shí)別執(zhí)行。
提到j(luò)ava的并發(fā),我們不由的就會(huì)想到j(luò)ava中常見(jiàn)的鍵字:volatile和synchronized,我們接下來(lái)就會(huì)從這兩個(gè)關(guān)機(jī)字展開(kāi)分析:
volatile的底層實(shí)現(xiàn)原理
synchronized的實(shí)現(xiàn)原理和應(yīng)用
volatile
說(shuō)到volatile,在java的面試中面試官可是最喜歡問(wèn)的問(wèn)題了??吹剿覀兪紫认氲降谋闶潜3志€程間的可見(jiàn)性,是一個(gè)輕量級(jí)的synchronized,在一些情況下它可以代替synchronized。
volatile的作用:
一個(gè)被volatie修飾的變量,java內(nèi)存模型會(huì)保證所有的線程看見(jiàn)的變量值是一致的。
volatile的工作原理:
我們可以定義一個(gè)volatile變量,并對(duì)他進(jìn)行賦值,并通過(guò)工具來(lái)獲取jit編譯器生成的匯編指令,我們會(huì)發(fā)現(xiàn)在對(duì)volatile變量進(jìn)行寫操作時(shí),會(huì)多出一條指令:以lock為前綴的指令:
lock為前綴的指令在多核處理器下回引發(fā)兩件事情:
①將當(dāng)前處理器緩存行的數(shù)據(jù)回寫到內(nèi)存中。
②這個(gè)回寫內(nèi)存的操作會(huì)使得在其他cpu里緩存了改內(nèi)存地址的數(shù)據(jù)無(wú)效。
當(dāng)我們知道了以上兩點(diǎn),我們就不難理解volatie變量的機(jī)制了。
在多處理器下,為了保證各個(gè)處理器的緩存是一致的,會(huì)實(shí)現(xiàn)緩存一致性協(xié)議,每個(gè)處理器通過(guò)嗅探在總線上的傳播的數(shù)據(jù)來(lái)檢查自己緩存的值是不是過(guò)期了。
synchronized
想到多線程的并發(fā),其實(shí)我第一個(gè)想到的便是這個(gè)synchronized,翻譯過(guò)來(lái)為同步,我們都知道它是一個(gè)重量級(jí)鎖,當(dāng)對(duì)一個(gè)方法或者代碼塊使用它時(shí),當(dāng)一個(gè)線程獲得了這個(gè)鎖,那么其它的線程就會(huì)陷入掛起狀態(tài),在java中也就表現(xiàn)為sleep狀態(tài),我們都知道線程的掛起和運(yùn)行時(shí)要轉(zhuǎn)入操作系統(tǒng)的內(nèi)核態(tài)的(與內(nèi)核態(tài)對(duì)應(yīng)的便是用戶態(tài)),這樣特別浪費(fèi)cpu資源,所以這個(gè)重量級(jí)鎖是名副其實(shí)的!
但是,java SE 1.6過(guò)后java的維護(hù)團(tuán)隊(duì)對(duì)它進(jìn)行了一系列的優(yōu)化(這些優(yōu)化后面一一講述),他也就沒(méi)那么“重”了,以前還有優(yōu)勢(shì)的可重入鎖也變得沒(méi)那么有優(yōu)勢(shì)了(ReentrantLock)。
一下我們就下列幾個(gè)方面講述synchronized:
利用synchronized實(shí)現(xiàn)同步的基礎(chǔ)
synchronized是如何實(shí)現(xiàn)鎖的
偏向鎖,輕量級(jí)鎖(自旋鎖),重量級(jí)鎖
鎖的升級(jí)
java如何實(shí)現(xiàn)原子操作
①利用synchronized實(shí)現(xiàn)同步的基礎(chǔ):
我們?cè)陂_(kāi)發(fā)中或者java的源碼中都能看見(jiàn)synchronized的身影,例如HashTable,StringBuilder等地方,常見(jiàn)有兩種方式:
Ⅰ丶同步方法
同步方法只需要在方法前加上synchronized便可,當(dāng)一個(gè)線程執(zhí)行它的時(shí)候其他線程便會(huì)陷入等待,直到它釋放鎖。對(duì)方法使用又可以分為兩種:對(duì)普通同步方法和對(duì)靜態(tài)方法,它們之間的差別是加鎖的對(duì)象不同,普通方法加鎖的位置是當(dāng)前的對(duì)象,而靜態(tài)方法加鎖的位置是當(dāng)前類的Class對(duì)象。
Ⅱ丶同步方法塊
同步方法塊加鎖的是Synchronized后括號(hào)里配置的對(duì)象,這個(gè)對(duì)象可以是一個(gè)值以及任何一個(gè)變量或者對(duì)象。
②synchronized是如何實(shí)現(xiàn)鎖的:
在jvm的規(guī)范中可以看到synchronized在jvm中的實(shí)現(xiàn)原理,jvm基于進(jìn)入和退出Monitor對(duì)象來(lái)實(shí)現(xiàn)同步方法和代碼塊的同步,代碼塊是使用monitorenter和monitorexit指令來(lái)實(shí)現(xiàn)的,而同步方法jvm規(guī)范里沒(méi)有具體給出,但是我相信具體的原理應(yīng)該相差不大,無(wú)非是將java源碼編譯為class文件,在class字節(jié)碼文件中對(duì)使用synchronized的方法進(jìn)行一個(gè)標(biāo)記,在字節(jié)碼引擎執(zhí)行這個(gè)方法的時(shí)候會(huì)對(duì)這個(gè)方法進(jìn)行同步處理。
③偏向鎖,輕量級(jí)鎖(自旋鎖),重量級(jí)鎖:
在講鎖之前我們需要知道java對(duì)象頭,java的對(duì)象頭:
synchronized使用的鎖是存儲(chǔ)在java對(duì)象頭里的,java對(duì)象頭里面有32bit/64bit(視操作系統(tǒng)的位數(shù)而定)長(zhǎng)度的MarkWord 里面存儲(chǔ)了對(duì)象的hashCode和鎖的信息等,在MarkWord中有2bit的空間來(lái)表示鎖的狀態(tài)00,01,10,11,分別表示輕量級(jí)鎖,偏向鎖,重量級(jí)鎖,GC標(biāo)記。
偏向鎖:偏向鎖也就人稱它為偏心鎖,從名字我們就可以看出來(lái),它是一個(gè)偏向某一個(gè)線程的鎖。
在實(shí)際的開(kāi)發(fā)中,我們發(fā)現(xiàn)多線程并發(fā),大多數(shù)執(zhí)行同步方法的都是同一個(gè)線程,出現(xiàn)多個(gè)線程爭(zhēng)搶一個(gè)方法的概率比較低,所以重復(fù)的獲取鎖和釋放鎖就會(huì)產(chǎn)生大量的資源浪費(fèi),所以為了讓線程獲得鎖的代價(jià)更低引入了偏向鎖,當(dāng)一個(gè)線程訪問(wèn)一個(gè)同步塊并獲得鎖時(shí),會(huì)在對(duì)象頭和線程的棧幀中的鎖記錄中存儲(chǔ)偏向鎖的線程ID,以后該線程進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作來(lái)進(jìn)行加鎖和解鎖,只需要簡(jiǎn)單的查看對(duì)象頭的MarkWord里是否還存有指向當(dāng)前的偏向鎖(在MarkWord中每個(gè)對(duì)象還有一個(gè)偏向鎖標(biāo)志位用來(lái)表示當(dāng)前對(duì)象是否支持偏向鎖,我們可以使用jvm參數(shù)來(lái)設(shè)定偏向鎖)。
關(guān)于偏向鎖的釋放,偏向鎖使用了等到存在競(jìng)爭(zhēng)時(shí)才釋放鎖的機(jī)制,所以當(dāng)有其他線程嘗試競(jìng)爭(zhēng)偏向鎖的時(shí)候持有偏向鎖的線程才會(huì)釋放鎖。
注意:在java6,7中偏向鎖是默認(rèn)啟動(dòng)的
輕量級(jí)鎖:
輕量級(jí)鎖就是在執(zhí)行同步塊之前,jvm會(huì)在當(dāng)前線程的棧幀中創(chuàng)建用于存儲(chǔ)鎖記錄的的空間,并將對(duì)象頭中的MarkWord復(fù)制到里面,然后線程將嘗試將對(duì)象頭內(nèi)的MarkWord替換為指向鎖記錄的指針,如果成功,當(dāng)前線程獲得鎖,如果失敗,表示其他線程競(jìng)爭(zhēng)鎖,當(dāng)前線程便自旋來(lái)獲得鎖。
④鎖的升級(jí):
當(dāng)前線程如果無(wú)法試用上面的方法獲得鎖,那么表示當(dāng)前的鎖存在競(jìng)爭(zhēng),鎖就會(huì)升級(jí)為重量級(jí)鎖。
輕量級(jí)鎖和偏向鎖的區(qū)別:
輕量級(jí)鎖是在無(wú)競(jìng)爭(zhēng)的情況下使用CAS操作去消除同步使用的互斥量,而偏向鎖就是在無(wú)競(jìng)爭(zhēng)的情況下把整個(gè)同步都去除,連CAS操作都不做!
⑤ java如何實(shí)現(xiàn)原子操作:
在了解java是如何實(shí)現(xiàn)原子操作之前,我們要知道處理器是如何實(shí)現(xiàn)原子操作的:
處理器一般分為兩種方法執(zhí)行原子操作:緩存加鎖和總線加鎖,其中緩存加鎖比較優(yōu)秀而總線加鎖則比較消耗資源。(關(guān)于兩種加鎖的方式我們這里不做過(guò)多解釋,具體在操作系統(tǒng)中有詳細(xì)的講解)
java使用(大多數(shù)情況下)循環(huán)CAS實(shí)現(xiàn)原子操作,但是使用CAS實(shí)現(xiàn)原子操作也會(huì)出現(xiàn)下面的一些經(jīng)典的問(wèn)題:
一)ABA問(wèn)題
jdk中提供AtomicStampedReference類來(lái)解決(提供檢查預(yù)期引用和預(yù)期標(biāo)志)
二)循環(huán)時(shí)間長(zhǎng)開(kāi)銷大
無(wú)法解決,這個(gè)是循環(huán)的通病
三)只能保證一個(gè)共享變量的原子操作
jdk中提供一個(gè)AtomicReference來(lái)解決,將多個(gè)共享變量放置在一個(gè)類中進(jìn)行CAS操作。
- Java LinkedHashMap 底層實(shí)現(xiàn)原理分析
- 在java中ArrayList集合底層的擴(kuò)容原理
- Java synchronize底層實(shí)現(xiàn)原理及優(yōu)化
- Java CAS底層實(shí)現(xiàn)原理實(shí)例詳解
- JAVA序列化和反序列化的底層實(shí)現(xiàn)原理解析
- JAVA字符串類型switch的底層原理詳析
- JAVA浮點(diǎn)數(shù)計(jì)算精度損失底層原理與解決方案
- Java集合的總體框架相關(guān)知識(shí)總結(jié)
- Java集合中contains方法的效率對(duì)比分析
- Java基礎(chǔ)學(xué)習(xí)之集合底層原理
相關(guān)文章
IDEA中使用Docker Compose容器編排的實(shí)現(xiàn)
這篇文章主要介紹了IDEA中使用Docker Compose容器編排的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07詳解Spring整合mybatis--Spring中的事務(wù)管理(xml形式)
這篇文章主要介紹了Spring整合mybatis--Spring中的事務(wù)管理(xml形式),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-11-11java獲取當(dāng)前日期和時(shí)間的二種方法分享
這篇文章主要介紹了java獲取當(dāng)前日期和時(shí)間的二種方法,需要的朋友可以參考下2014-03-03關(guān)于Java中靜態(tài)代碼塊的執(zhí)行淺析
這篇文章主要給大家介紹了關(guān)于Java中靜態(tài)代碼塊執(zhí)行的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09Java設(shè)計(jì)模式之原型模式詳細(xì)解析
這篇文章主要介紹了Java設(shè)計(jì)模式之原型模式詳細(xì)解析,原型模式就是用一個(gè)已經(jīng)創(chuàng)建的實(shí)例作為原型,通過(guò)復(fù)制該原型對(duì)象來(lái)創(chuàng)建一個(gè)和原型對(duì)象相同的新對(duì)象,需要的朋友可以參考下2023-11-11詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)
這篇文章主要介紹了詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)的相關(guān)資料,需要的朋友可以參考下2017-06-06Java如何讀寫Properties配置文件(Properties類)
這篇文章主要介紹了Java如何讀寫Properties配置文件(Properties類),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05