Java 線程同步與互斥,線程安全蚁孔,Java鎖

多線程三個(gè)特征:原子性奶赔、可見性以及有序性.

同步鎖 /并發(fā)鎖/ 讀寫鎖,顯示鎖杠氢, ReentrantLock與Condition.

> 線程的同步與互斥?(同步線程與異步線程站刑,線程同步和異步問題)

Java 虛擬機(jī)中的同步(Synchronization)基于進(jìn)入和退出管程(Monitor)對(duì)象實(shí)現(xiàn), 無論是顯式同步(有明確的 monitorenter 和 monitorexit 指令,即同步代碼塊)還是隱式同步都是如此鼻百。在 Java 語言中绞旅,同步用的最多的地方可能是被 synchronized 修飾的同步方法。同步方法 并不是由 monitorenter 和 monitorexit 指令來實(shí)現(xiàn)同步的温艇,而是由方法調(diào)用指令讀取運(yùn)行時(shí)常量池中方法的 ACC_SYNCHRONIZED 標(biāo)志來隱式實(shí)現(xiàn)的.

1.同步:假設(shè)現(xiàn)有線程A和線程B因悲,線程A需要往緩沖區(qū)寫數(shù)據(jù),線程B需要從緩沖區(qū)讀數(shù)據(jù)勺爱,但他們之間存在一種制約關(guān)系晃琳,即當(dāng)線程A寫的時(shí)候,B不能來拿數(shù)據(jù)琐鲁;B在拿數(shù)據(jù)的時(shí)候A不能往緩沖區(qū)寫卫旱,也就是說,只有當(dāng)A寫完數(shù)據(jù)(或B取走數(shù)據(jù))围段,B才能來讀數(shù)據(jù)(或A才能往里寫數(shù)據(jù))誊涯。這種關(guān)系就是一種線程的同步關(guān)系。

同步關(guān)系則是多個(gè)線程彼此合作蒜撮,通過一定的邏輯關(guān)系來共同完成一個(gè)任務(wù)暴构。一般來說,同步關(guān)系中往往包含互斥段磨,同時(shí)對(duì)臨界區(qū)的資源會(huì)按照某種邏輯順序進(jìn)行訪問取逾。如先生產(chǎn)后使用。

同步并沒有通過指令monitorenter和monitorexit來完成(理論上其實(shí)也可以通過這兩條指令來實(shí)現(xiàn))苹支,不過相對(duì)于普通方法砾隅,其常量池中多了ACC_SYNCHRONIZED標(biāo)示符。JVM就是根據(jù)該標(biāo)示符來實(shí)現(xiàn)方法的同步的:當(dāng)方法調(diào)用時(shí)债蜜,調(diào)用指令將會(huì)檢查方法的 ACC_SYNCHRONIZED 訪問標(biāo)志是否被設(shè)置晴埂,如果設(shè)置了究反,執(zhí)行線程將先獲取monitor,獲取成功之后才能執(zhí)行方法體儒洛,方法執(zhí)行完后再釋放monitor精耐。在方法執(zhí)行期間,其他任何線程都無法再獲得同一個(gè)monitor對(duì)象琅锻。 其實(shí)本質(zhì)上沒有區(qū)別卦停,只是方法的同步是一種隱式的方式來實(shí)現(xiàn),無需通過字節(jié)碼來完成恼蓬。

2.互斥:對(duì)于線程A和線程B來講惊完,在同一時(shí)刻,只允許一個(gè)線程對(duì)臨界資源進(jìn)行操作处硬,即當(dāng)A進(jìn)入臨界區(qū)對(duì)資源操作時(shí)小槐,B就必須等待;當(dāng)A執(zhí)行完荷辕,退出臨界區(qū)后凿跳,B才能對(duì)臨界資源進(jìn)行操作。

所謂互斥桐腌,就是不同線程通過競(jìng)爭(zhēng)進(jìn)入臨界區(qū)(共享的數(shù)據(jù)和硬件資源)拄显,為了防止訪問沖突,在有限的時(shí)間內(nèi)只允許其中之一獨(dú)占性的使用共享資源案站。如不允許同時(shí)寫躬审;

3.總的來說,兩者的區(qū)別就是:

互斥是通過競(jìng)爭(zhēng)對(duì)資源的獨(dú)占使用蟆盐,彼此之間不需要知道對(duì)方的存在承边,執(zhí)行順序是一個(gè)亂序。

同步是協(xié)調(diào)多個(gè)相互關(guān)聯(lián)線程合作完成任務(wù)石挂,彼此之間知道對(duì)方存在博助,執(zhí)行順序往往是有序的。

> synchronized在靜態(tài)方法上表示調(diào)用前要獲得類的鎖痹愚,而在非靜態(tài)方法上表示調(diào)用此方法前要獲得對(duì)象的鎖富岳。

public class StaticSynDemo {

private static String a="test";

public void print2(String b){

synchronized (this)?{//取得StaticSynDemo實(shí)例化后對(duì)象的鎖

System.out.println(b+a);

}

}

public?static?void print4(String b){

synchronized (StaticSynDemo.class)?{ //取得StaticSynDemo.class類的鎖

System.out.println(b+a);

}

}

}

同步代碼塊或同步對(duì)象:synchronized 方法和 synchronized 塊。synchronized(this)拯腮、synchronized(*.class)與synchronized(任意對(duì)象)這幾種類型同步方法:

同步synchronized(*.class)代碼塊的作用其實(shí)和synchronized static方法作用一樣窖式。Class鎖對(duì)類的所有對(duì)象實(shí)例起作用。

this關(guān)鍵字代表類的一個(gè)對(duì)象动壤,所以其內(nèi)存鎖是針對(duì)相同對(duì)象的互斥操作萝喘,而static成員屬于類專有,其內(nèi)存空間為該類所有成員共有,這就導(dǎo)致synchronized()對(duì)static成員加鎖阁簸,相當(dāng)于對(duì)類加鎖爬早,也就是在該類的所有成員間實(shí)現(xiàn)互斥,在同一時(shí)間只有一個(gè)線程可訪問該類的實(shí)例启妹。

Synchronized和Static Synchronized區(qū)別:

一個(gè)是實(shí)例鎖(鎖在某一個(gè)實(shí)例對(duì)象上筛严,如果該類是單例,那么該鎖也具有全局鎖的概念)翅溺,一個(gè)是全局鎖(該鎖針對(duì)的是類脑漫,無論實(shí)例多少個(gè)對(duì)象髓抑,那么線程都共享該鎖)咙崎。

實(shí)例鎖對(duì)應(yīng)的就是synchronized關(guān)鍵字,而類鎖(全局鎖)對(duì)應(yīng)的就是static synchronized(或者是鎖在該類的class或者classloader對(duì)象上)吨拍。

synchronized(this)與synchronized(static XXX)的區(qū)別了褪猛,synchronized就是針對(duì)內(nèi)存區(qū)塊申請(qǐng)內(nèi)存鎖,this關(guān)鍵字代表類的一個(gè)對(duì)象羹饰,所以其內(nèi)存鎖是針對(duì)相同對(duì)象的互斥操作伊滋,而static成員屬于類專有,其內(nèi)存空間為該類所有成員共有队秩,這就導(dǎo)致synchronized()對(duì)static成員加鎖笑旺,相當(dāng)于對(duì)類加鎖,也就是在該類的所有成員間實(shí)現(xiàn)互斥馍资,在同一時(shí)間只有一個(gè)線程可訪問該類的實(shí)例筒主。

synchronized是通過同一時(shí)刻只有一個(gè)線程執(zhí)行共享代碼來保證多線程三個(gè)特征的;

volatile 變量具有 synchronized 的可見性特性,禁止指令重排鸟蟹,但是不具備原子特性乌妙。使用volatile變量,必須同時(shí)滿足下面兩個(gè)條件:

1.對(duì)變量的寫操作不依賴于當(dāng)前值建钥。

2.該變量沒有包含在具有其他變量的不變式中藤韵。

> 線程安全

造成線程安全問題的主要誘因有兩點(diǎn):

一是存在共享數(shù)據(jù)(也稱臨界資源),

二是存在多條線程共同操作共享數(shù)據(jù)熊经。

在單線程中不會(huì)出現(xiàn)線程安全問題泽艘,而在多線程編程中,有可能會(huì)出現(xiàn)同時(shí)訪問同一個(gè)資源的情況镐依,這種資源可以是各種類型的的資源:一個(gè)變量匹涮、一個(gè)對(duì)象、一個(gè)文件馋吗、一個(gè)數(shù)據(jù)庫表等焕盟,而當(dāng)多個(gè)線程同時(shí)訪問同一個(gè)資源的時(shí)候,就會(huì)存在一個(gè)問題:

由于每個(gè)線程執(zhí)行的過程是不可控的,所以很可能導(dǎo)致最終的結(jié)果與實(shí)際上的愿望相違背或者直接導(dǎo)致程序出錯(cuò)脚翘。線程安全問題灼卢,即多個(gè)線程同時(shí)訪問一個(gè)資源時(shí),會(huì)導(dǎo)致程序運(yùn)行結(jié)果并不是想看到的結(jié)果来农。這里面鞋真,這個(gè)資源被稱為:臨界資源(也有稱為共享資源)。

也就是說沃于,當(dāng)多個(gè)線程同時(shí)訪問臨界資源(一個(gè)對(duì)象涩咖,對(duì)象中的屬性,一個(gè)文件繁莹,一個(gè)數(shù)據(jù)庫等)時(shí)檩互,就可能會(huì)產(chǎn)生線程安全問題。不過咨演,當(dāng)多個(gè)線程執(zhí)行一個(gè)方法闸昨,方法內(nèi)部的局部變量并不是臨界資源,因?yàn)榉椒ㄊ窃跅I蠄?zhí)行的薄风,而Java棧是線程私有的饵较,因此不會(huì)產(chǎn)生線程安全問題。

-- 如何解決線程安全問題的呢遭赂?

基本上所有的并發(fā)模式在解決線程安全問題時(shí)循诉,都采用“序列化訪問臨界資源”的方案,即在同一時(shí)刻撇他,只能有一個(gè)線程訪問臨界資源茄猫,也稱作同步互斥訪問。通常來說逆粹,是在訪問臨界資源的代碼前面加上一個(gè)鎖募疮,當(dāng)訪問完臨界資源后釋放鎖,讓其他線程繼續(xù)訪問僻弹。在Java中阿浓,提供了兩種方式來實(shí)現(xiàn)同步互斥訪問:synchronized和Lock。

在了解synchronized關(guān)鍵字的使用方法之前蹋绽,我們先來看一個(gè)概念:互斥鎖芭毙,顧名思義:能到達(dá)到互斥訪問目的的鎖。舉個(gè)簡(jiǎn)單的例子:如果對(duì)臨界資源加上互斥鎖卸耘,當(dāng)一個(gè)線程在訪問該臨界資源時(shí)退敦,其他線程便只能等待。

在Java中蚣抗,每一個(gè)對(duì)象都擁有一個(gè)鎖標(biāo)記(monitor)侈百,也稱為監(jiān)視器,多線程同時(shí)訪問某個(gè)對(duì)象時(shí),線程只有獲取了該對(duì)象的鎖才能訪問钝域。

在Java中讽坏,可以使用synchronized關(guān)鍵字來標(biāo)記一個(gè)方法或者代碼塊,當(dāng)某個(gè)線程調(diào)用該對(duì)象的synchronized方法或者訪問synchronized代碼塊時(shí)例证,這個(gè)線程便獲得了該對(duì)象的鎖路呜,其他線程暫時(shí)無法訪問這個(gè)方法,只有等待這個(gè)方法執(zhí)行完畢或者代碼塊執(zhí)行完畢织咧,這個(gè)線程才會(huì)釋放該對(duì)象的鎖胀葱,其他線程才能執(zhí)行這個(gè)方法或者代碼塊。

> 同步鎖 /并發(fā)鎖/ 讀寫鎖

互斥鎖的本質(zhì): 首先需要明確一點(diǎn)笙蒙,互斥鎖實(shí)際上是一種變量抵屿,在使用互斥鎖時(shí),實(shí)際上是對(duì)這個(gè)變量進(jìn)行置0置1操作并進(jìn)行判斷使得線程能夠獲得鎖或釋放鎖手趣。

-- 死鎖產(chǎn)生的條件:?

1晌该、互斥屬性:即每次只能有一個(gè)線程占用資源肥荔。

2绿渣、請(qǐng)求與保持:即已經(jīng)申請(qǐng)到鎖資源的線程可以繼續(xù)申請(qǐng)。在這種情況下燕耿,一個(gè)線程也可以產(chǎn)生死鎖情況中符,即抱著鎖找鎖。

3誉帅、不可剝奪:線程已經(jīng)得到所資源淀散,在沒有自己主動(dòng)釋放之前,不能被強(qiáng)行剝奪蚜锨。

4档插、循環(huán)等待:多個(gè)線程形成環(huán)路等待,每個(gè)線程都在等待相鄰線程的鎖資源亚再。

-- 死鎖的避免:?

1郭膛、既然死鎖的產(chǎn)生是由于使用了鎖,那么在能不使用鎖的情況下就盡量不使用氛悬,如果有多種方案都能實(shí)現(xiàn)则剃,那么盡量不選用帶鎖的這種方案

2、盡量避免同時(shí)獲得多把鎖如捅,如果有必要棍现,就要保證獲得鎖的順序相同。

> Java鎖:無鎖狀態(tài)镜遣、偏向鎖己肮、輕量級(jí)鎖和重量級(jí)鎖

-- 鎖的使用場(chǎng)景?

由于偏向鎖在線程存在競(jìng)爭(zhēng)的時(shí)候會(huì)帶來額外的性能開銷,所以偏向鎖適用于只有一個(gè)線程方法同步快的情況;輕量級(jí)鎖在線程競(jìng)爭(zhēng)鎖的情況下不會(huì)導(dǎo)致線程阻塞谎僻,但是會(huì)通過自旋消耗CPU窖剑,所以輕量級(jí)鎖適用于追求響應(yīng)時(shí)間的情況。重量級(jí)鎖線程競(jìng)爭(zhēng)不會(huì)使用自旋戈稿,但是線程競(jìng)爭(zhēng)會(huì)導(dǎo)致阻塞西土,所以響應(yīng)時(shí)間比較慢,重量級(jí)鎖一般使用在追求吞吐量的情況鞍盗。

-- 當(dāng)前常用的多線程同步機(jī)制可以分為下面三種類型:?

1.volatile 變量:輕量級(jí)多線程同步機(jī)制需了,不會(huì)引起上下文切換和線程調(diào)度。僅提供內(nèi)存可見性保證般甲,不提供原子性肋乍。

2.CAS 原子指令:輕量級(jí)多線程同步機(jī)制,不會(huì)引起上下文切換和線程調(diào)度敷存。它同時(shí)提供內(nèi)存可見性和原子化更新保證墓造。

3.內(nèi)部鎖和顯式鎖:重量級(jí)多線程同步機(jī)制,可能會(huì)引起上下文切換和線程調(diào)度锚烦,它同時(shí)提供內(nèi)存可見性和原子性觅闽。

?-- java同步機(jī)制: volatile、synchronized和final.

Java? 語言包含兩種內(nèi)在的同步機(jī)制:同步塊(或方法)和 volatile 變量涮俄。這兩種機(jī)制的提出都是為了實(shí)現(xiàn)代碼線程的安全性蛉拙。其中 Volatile 變量的同步性較差(但有時(shí)它更簡(jiǎn)單并且開銷更低),而且其使用也更容易出錯(cuò)彻亲。

Java 語言中的 volatile 變量可以被看作是一種 “程度較輕的 synchronized”孕锄;與 synchronized 塊相比,volatile 變量所需的編碼較少苞尝,并且運(yùn)行時(shí)開銷也較少畸肆,但是它所能實(shí)現(xiàn)的功能也僅是 synchronized 的一部分。

鎖提供了兩種主要特性:互斥(mutual exclusion) 和可見性(visibility)宙址≈崞辏互斥即一次只允許一個(gè)線程持有某個(gè)特定的鎖,因此可使用該特性實(shí)現(xiàn)對(duì)共享數(shù)據(jù)的協(xié)調(diào)訪問協(xié)議曼氛,這樣豁辉,一次就只有一個(gè)線程能夠使用該共享數(shù)據(jù)∫ɑ迹可見性要更加復(fù)雜一些徽级,它必須確保釋放鎖之前對(duì)共享數(shù)據(jù)做出的更改對(duì)于隨后獲得該鎖的另一個(gè)線程是可見的 —— 如果沒有同步機(jī)制提供的這種可見性保證,線程看到的共享變量可能是修改前的值或不一致的值聊浅,這將引發(fā)許多嚴(yán)重問題餐抢。

synchronized屬于重量級(jí)鎖现使,效率低下,因?yàn)楸O(jiān)視器鎖(monitor)是依賴于底層的操作系統(tǒng)的Mutex Lock來實(shí)現(xiàn)的旷痕,而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換時(shí)需要從用戶態(tài)轉(zhuǎn)換到核心態(tài)碳锈,這個(gè)狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間,時(shí)間成本相對(duì)較高欺抗,這也是為什么早期的synchronized效率低的原因售碳。慶幸的是在Java 6之后Java官方對(duì)從JVM層面對(duì)synchronized較大優(yōu)化,所以現(xiàn)在的synchronized鎖效率也優(yōu)化得很不錯(cuò)了绞呈,Java 6之后贸人,為了減少獲得鎖和釋放鎖所帶來的性能消耗,引入了輕量級(jí)鎖和偏向鎖佃声,接下來我們將簡(jiǎn)單了解一下Java官方在JVM層面對(duì)synchronized鎖的優(yōu)化艺智。

鎖的狀態(tài)總共有四種,無鎖狀態(tài)圾亏、偏向鎖十拣、輕量級(jí)鎖和重量級(jí)鎖。隨著鎖的競(jìng)爭(zhēng)志鹃,鎖可以從偏向鎖升級(jí)到輕量級(jí)鎖夭问,再升級(jí)的重量級(jí)鎖辑甜,但是鎖的升級(jí)是單向的,也就是說只能從低到高升級(jí)暮屡,不會(huì)出現(xiàn)鎖的降級(jí).讀寫鎖特點(diǎn):

1)多個(gè)讀者可以同時(shí)進(jìn)行讀

2)寫者必須互斥(只允許一個(gè)寫者寫鲤竹,也不能讀者寫者同時(shí)進(jìn)行)

3)寫者優(yōu)先于讀者(一旦有寫者,則后續(xù)讀者必須等待征炼,喚醒時(shí)優(yōu)先考慮寫者)

互斥鎖特點(diǎn):一次只能一個(gè)線程擁有互斥鎖,其他線程只有等待

-- 使用同步機(jī)制獲取互斥鎖的情況,進(jìn)行幾點(diǎn)說明:

1淳玩、如果同一個(gè)方法內(nèi)同時(shí)有兩個(gè)或更多線程,則每個(gè)線程有自己的局部變量拷貝非竿。

2蜕着、類的每個(gè)實(shí)例都有自己的對(duì)象級(jí)別鎖。當(dāng)一個(gè)線程訪問實(shí)例對(duì)象中的synchronized同步代碼塊或同步方法時(shí)红柱,該線程便獲取了該實(shí)例的對(duì)象級(jí)別鎖承匣,其他線程這時(shí)如果要訪問synchronized同步代碼塊或同步方法,便需要阻塞等待锤悄,直到前面的線程從同步代碼塊或方法中退出韧骗,釋放掉了該對(duì)象級(jí)別鎖。

3零聚、訪問同一個(gè)類的不同實(shí)例對(duì)象中的同步代碼塊袍暴,不存在阻塞等待獲取對(duì)象鎖的問題些侍,因?yàn)樗鼈儷@取的是各自實(shí)例的對(duì)象級(jí)別鎖,相互之間沒有影響政模。

4岗宣、持有一個(gè)對(duì)象級(jí)別鎖不會(huì)阻止該線程被交換出來,也不會(huì)阻塞其他線程訪問同一示例對(duì)象中的非synchronized代碼淋样。當(dāng)一個(gè)線程A持有一個(gè)對(duì)象級(jí)別鎖(即進(jìn)入了synchronized修飾的代碼塊或方法中)時(shí)耗式,線程也有可能被交換出去,此時(shí)線程B有可能獲取執(zhí)行該對(duì)象中代碼的時(shí)間趁猴,但它只能執(zhí)行非同步代碼(沒有用synchronized修飾)纽什,當(dāng)執(zhí)行到同步代碼時(shí),便會(huì)被阻塞躲叼,此時(shí)可能線程規(guī)劃器又讓A線程運(yùn)行芦缰,A線程繼續(xù)持有對(duì)象級(jí)別鎖,當(dāng)A線程退出同步代碼時(shí)(即釋放了對(duì)象級(jí)別鎖)枫慷,如果B線程此時(shí)再運(yùn)行让蕾,便會(huì)獲得該對(duì)象級(jí)別鎖,從而執(zhí)行synchronized中的代碼或听。

5探孝、持有對(duì)象級(jí)別鎖的線程會(huì)讓其他線程阻塞在所有的synchronized代碼外。例如誉裆,在一個(gè)類中有三個(gè)synchronized方法a顿颅,b,c足丢,當(dāng)線程A正在執(zhí)行一個(gè)實(shí)例對(duì)象M中的方法a時(shí)粱腻,它便獲得了該對(duì)象級(jí)別鎖,那么其他的線程在執(zhí)行同一實(shí)例對(duì)象(即對(duì)象M)中的代碼時(shí)斩跌,便會(huì)在所有的synchronized方法處阻塞绍些,即在方法a,b耀鸦,c處都要被阻塞柬批,等線程A釋放掉對(duì)象級(jí)別鎖時(shí),其他的線程才可以去執(zhí)行方法a袖订,b或者c中的代碼氮帐,從而獲得該對(duì)象級(jí)別鎖。

6洛姑、使用synchronized(obj)同步語句塊上沐,可以獲取指定對(duì)象上的對(duì)象級(jí)別鎖。obj為對(duì)象的引用吏口,如果獲取了obj對(duì)象上的對(duì)象級(jí)別鎖奄容,在并發(fā)訪問obj對(duì)象時(shí)時(shí)冰更,便會(huì)在其synchronized代碼處阻塞等待,直到獲取到該obj對(duì)象的對(duì)象級(jí)別鎖昂勒。當(dāng)obj為this時(shí)蜀细,便是獲取當(dāng)前對(duì)象的對(duì)象級(jí)別鎖。

7戈盈、類級(jí)別鎖被特定類的所有示例共享奠衔,它用于控制對(duì)static成員變量以及static方法的并發(fā)訪問。具體用法與對(duì)象級(jí)別鎖相似塘娶。

8归斤、互斥是實(shí)現(xiàn)同步的一種手段,臨界區(qū)刁岸、互斥量和信號(hào)量都是主要的互斥實(shí)現(xiàn)方式脏里。synchronized關(guān)鍵字經(jīng)過編譯后,會(huì)在同步塊的前后分別形成monitorenter和monitorexit這兩個(gè)字節(jié)碼指令虹曙。根據(jù)虛擬機(jī)規(guī)范的要求迫横,在執(zhí)行monitorenter指令時(shí),首先要嘗試獲取對(duì)象的鎖酝碳,如果獲得了鎖矾踱,把鎖的計(jì)數(shù)器加1,相應(yīng)地疏哗,在執(zhí)行monitorexit指令時(shí)會(huì)將鎖計(jì)數(shù)器減1呛讲,當(dāng)計(jì)數(shù)器為0時(shí),鎖便被釋放了返奉。由于synchronized同步塊對(duì)同一個(gè)線程是可重入的贝搁,因此一個(gè)線程可以多次獲得同一個(gè)對(duì)象的互斥鎖,同樣衡瓶,要釋放相應(yīng)次數(shù)的該互斥鎖徘公,才能最終釋放掉該鎖。

-- 無鎖哮针、偏向鎖、輕量級(jí)鎖和重量級(jí)鎖:

在JDK 1.6中引入了“偏向鎖”和“輕量級(jí)鎖“坦袍。鎖一共有四種狀態(tài):無鎖十厢、偏向鎖、輕量級(jí)鎖和重量級(jí)鎖捂齐。鎖只能升級(jí)蛮放,不能降級(jí)。當(dāng)對(duì)鎖的競(jìng)爭(zhēng)加劇的時(shí)候奠宜,鎖會(huì)發(fā)生升級(jí)包颁。

1.偏向鎖

之所以引入偏向鎖瞻想,是為了讓線程獲得鎖的代價(jià)更低。當(dāng)一個(gè)線程訪問同步塊并獲取鎖的時(shí)候娩嚼,會(huì)在對(duì)象的對(duì)象頭(對(duì)象頭包括兩部分的信息:一部分是”Mark Word“蘑险,主要存放的是哈希碼、對(duì)象的分代年齡岳悟、鎖的標(biāo)記等信息佃迄;另一部分是對(duì)象的類型指針)和棧幀中的鎖記錄中存儲(chǔ)鎖偏向的ID,以后該線程在進(jìn)入方法的同步塊的時(shí)候贵少,就檢查這個(gè)ID(可以理解為一種標(biāo)記呵俏,是一種身份的標(biāo)識(shí)),如果測(cè)試成功滔灶,表明對(duì)象已經(jīng)獲得了鎖普碎;如果測(cè)試失敗,繼續(xù)測(cè)試偏向鎖的標(biāo)識(shí)是否設(shè)置為1(1的話就是偏向鎖)录平,如果沒有則使用CAS(Compare And Swap)鎖麻车。

2.輕量級(jí)鎖

分為加鎖和解鎖。當(dāng)線程執(zhí)行到同步塊之前萄涯,JVM會(huì)首先檢查當(dāng)前線程的棧幀中創(chuàng)建用于存儲(chǔ)記錄鎖記錄的空間绪氛,并將對(duì)象頭中Mark Word復(fù)制到鎖記錄中,也稱為Displaced Mark Word涝影,然后線程嘗試使用CAS將對(duì)象頭中的Mark Word替換為指向鎖記錄的指針枣察。如果成功,則線程獲得鎖燃逻,否則當(dāng)前線程嘗試使用自旋來獲取鎖序目。這就是加鎖的過程。

這里多次提到CAS伯襟,那么CAS是個(gè)什么鬼猿涨?CAS是Compare and swap(比較和替換)的簡(jiǎn)寫,具體而言就是:當(dāng)進(jìn)行CAS操作的時(shí)候姆怪,需要輸入兩個(gè)數(shù)值叛赚,一個(gè)是舊值,該舊值是原來的值稽揭,另一個(gè)是新值俺附,也就是發(fā)生改變的值,得到這兩個(gè)值后溪掀,在CAS操作期間會(huì)去比較舊值是否發(fā)生變化事镣,如果沒有發(fā)生變化就用新值進(jìn)行替換,如果發(fā)生了變化就不進(jìn)行替換揪胃。

那么解鎖的過程又是怎樣的呢璃哟?就是使用CAS操作將Displaced Mark Word替換回對(duì)象頭氛琢,如果成功,則表示沒有競(jìng)爭(zhēng)發(fā)生随闪。如果失敗阳似,表示當(dāng)前鎖存在競(jìng)爭(zhēng),鎖就會(huì)膨脹蕴掏,膨脹的結(jié)果是導(dǎo)致鎖的升級(jí)障般,并進(jìn)入阻塞狀態(tài)。直到需要釋放鎖的線程釋放鎖并喚醒其他等待的線程盛杰。

Java 理論與實(shí)踐: JDK 5.0 中更靈活挽荡、更具可伸縮性的鎖定機(jī)制-- http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html

Java 語言包括了跨線程傳達(dá)并發(fā)性約束的構(gòu)造 —— synchronized 和 volatile 。把代碼塊聲明為 synchronized即供,有兩個(gè)重要后果定拟,通常是指該代碼具有 原子性(atomicity)和 可見性(visibility)。

Condition是配合Lock使用的逗嫡,而wait/notify是配合synchronized使用的青自。比較兩種實(shí)現(xiàn)方式,其實(shí)就是比較Lock和synchronized兩種同步機(jī)制的區(qū)別驱证。 ReentrantLock 和 synchronized 的可伸縮性.

-- 可重入代碼, 重入鎖ReetrantLock

可重入代碼又稱為“純代碼”延窜,是一種允許多個(gè)進(jìn)程訪問的代碼,因此抹锄,可重入代碼是一種不允許任何進(jìn)程對(duì)它進(jìn)行修改的代碼 逆瑞。為了能修改,訪問純代碼的

進(jìn)程伙单,把執(zhí)行中可能改變的部分拷貝到該數(shù)據(jù)區(qū)获高,只需對(duì)該數(shù)據(jù)區(qū)中的內(nèi)容進(jìn)行修改,并不去改變共享的代碼吻育,這時(shí)的可共享代碼即成為可重入碼念秧。

synchronized一般都是配合wait()和notify()一起使用的;通過重入鎖 的newCondition()創(chuàng)建一個(gè)Condition布疼, Condition 的await()和signal()

并發(fā)編程都是離不開 同步的摊趾,而同步最簡(jiǎn)單的莫過于synchronized關(guān)鍵字了;ReentrantLock重入鎖

Condition condition = lock.newCondition(); condition.await(); condition.signal();

ReentrantLock lock = new ReentrantLock();lock.lock();lock.unlock();lock.tryLock() lock.tryLock(10, TimeUnit.SECONDS);

若一個(gè)程序或子程序可以安全的被并行執(zhí)行游两,則稱其為可重入(reentrant或re-entrant)的严就;即,當(dāng)該子程序正在運(yùn)行時(shí)器罐,可以再次進(jìn)入并執(zhí)行它。若一個(gè)函數(shù)是可重入的渐行,則該函數(shù):

不能含有靜態(tài)(全局)非常量數(shù)據(jù)轰坊。

不能返回靜態(tài)(全局)非常量數(shù)據(jù)的地址铸董。

只能處理由調(diào)用者提供的數(shù)據(jù)。

不能依賴于單實(shí)例模式資源的鎖肴沫。

不能調(diào)用不可重入的函數(shù)粟害。

多用戶/對(duì)象/進(jìn)程優(yōu)先級(jí)'以及多進(jìn)程一般會(huì)使得對(duì)可重入代碼的控制變得復(fù)雜。同時(shí)颤芬,IO代碼通常不是可重入的悲幅,因?yàn)樗麄円蕾囉谙翊疟P這樣共享的、單獨(dú)的資源站蝠。

要提供可重入性代碼方法是:編寫的函數(shù)都只會(huì)影響到局部變量汰具,而不能改變?nèi)值臄?shù)據(jù)結(jié)構(gòu)。這樣的函數(shù)都被稱作可重入函數(shù)菱魔。但是一個(gè)可重入內(nèi)核不僅有這些可重入函數(shù)留荔。Linux內(nèi)核都是可重入的。由于在訪問I/O等共享資源澜倦,內(nèi)核還要有不可重入函數(shù)聚蝶。此時(shí),Linux內(nèi)核就要使用鎖機(jī)制藻治,來保證在某一時(shí)間內(nèi)只有一個(gè)進(jìn)程可執(zhí)行該不可重入代碼碘勉,也就是分時(shí)共享的辦法,來實(shí)現(xiàn)可重入的內(nèi)核桩卵。

重入鎖ReetrantLock验靡,JDK 1.5新增的類,實(shí)現(xiàn)了Lock接口吸占,作用與synchronized關(guān)鍵字相當(dāng)晴叨,但比synchronized更加靈活。synchronized在等待獲取鎖時(shí)是不可中的矾屯。

AQS兼蕊,AbstractQueuedSynchronizer又稱為隊(duì)列同步器(后面簡(jiǎn)稱AQS),它是用來構(gòu)建鎖或其他同步組件的基礎(chǔ)框架件蚕,內(nèi)部通過一個(gè)int類型的成員變量state來控制同步狀態(tài),當(dāng)state=0時(shí)孙技,則說明沒有任何線程占有共享資源的鎖,當(dāng)state=1時(shí)排作,則說明有線程目前正在使用共享變量牵啦,其他線程必須加入同步隊(duì)列進(jìn)行等待,AQS內(nèi)部通過內(nèi)部類Node構(gòu)成FIFO的同步隊(duì)列來完成線程獲取鎖的排隊(duì)工作妄痪,同時(shí)利用內(nèi)部類ConditionObject構(gòu)建等待隊(duì)列哈雏,當(dāng)Condition調(diào)用wait()方法后,線程將會(huì)加入等待隊(duì)列中,而當(dāng)Condition調(diào)用signal()方法后裳瘪,線程將從等待隊(duì)列轉(zhuǎn)移動(dòng)同步隊(duì)列中進(jìn)行鎖競(jìng)爭(zhēng)土浸。注意這里涉及到兩種隊(duì)列,一種的同步隊(duì)列彭羹,當(dāng)線程請(qǐng)求鎖而等待的后將加入同步隊(duì)列等待黄伊,而另一種則是等待隊(duì)列(可有多個(gè)),通過Condition調(diào)用await()方法釋放鎖后派殷,將加入等待隊(duì)列还最。

AQS作為基礎(chǔ)組件,對(duì)于鎖的實(shí)現(xiàn)存在兩種不同的模式毡惜,即共享模式(如Semaphore)和獨(dú)占模式(如ReetrantLock)拓轻,無論是共享模式還是獨(dú)占模式的實(shí)現(xiàn)類,其內(nèi)部都是基于AQS實(shí)現(xiàn)的虱黄,也都維持著一個(gè)虛擬的同步隊(duì)列悦即,當(dāng)請(qǐng)求鎖的線程超過現(xiàn)有模式的限制時(shí),會(huì)將線程包裝成Node結(jié)點(diǎn)并將線程當(dāng)前必要的信息存儲(chǔ)到node結(jié)點(diǎn)中橱乱,然后加入同步隊(duì)列等會(huì)獲取鎖辜梳,而這系列操作都有AQS協(xié)助我們完成,這也是作為基礎(chǔ)組件的原因泳叠,無論是Semaphore還是ReetrantLock作瞄,其內(nèi)部絕大多數(shù)方法都是間接調(diào)用AQS完成的。從設(shè)計(jì)模式角度來看危纫,AQS采用的模板模式的方式構(gòu)建的宗挥,其內(nèi)部除了提供并發(fā)操作核心方法以及同步隊(duì)列操作外,還提供了一些模板方法讓子類自己實(shí)現(xiàn)种蝶,如加鎖操作以及解鎖操作契耿。

在JDK 1.6之后,虛擬機(jī)對(duì)于synchronized關(guān)鍵字進(jìn)行整體優(yōu)化后螃征,在性能上synchronized與ReentrantLock已沒有明顯差距搪桂,因此在使用選擇上,需要根據(jù)場(chǎng)景而定盯滚,大部分情況下我們依然建議是synchronized關(guān)鍵字踢械,原因之一是使用方便語義清晰,二是性能上虛擬機(jī)已為我們自動(dòng)優(yōu)化魄藕。而ReentrantLock提供了多樣化的同步特性内列,如超時(shí)獲取鎖、可以被中斷獲取鎖(synchronized的同步是不能中斷的)背率、等待喚醒機(jī)制的多個(gè)條件變量(Condition)等话瞧,因此當(dāng)我們確實(shí)需要使用到這些功能是嫩与,可以選擇ReentrantLock.


加Java架構(gòu)師群獲取Java工程化、高性能及分布式移稳、高性能蕴纳、深入淺出。高架構(gòu)个粱。性能調(diào)優(yōu)、Spring翻翩,MyBatis都许,Netty源碼分析和大數(shù)據(jù)等多個(gè)知識(shí)點(diǎn)高級(jí)進(jìn)階干貨的直播免費(fèi)學(xué)習(xí)權(quán)限 都是大牛帶飛 讓你少走很多的彎路的 群..號(hào)是:855801563 對(duì)了 小白勿進(jìn) 最好是有開發(fā)經(jīng)驗(yàn)

注:加群要求

1、具有工作經(jīng)驗(yàn)的嫂冻,面對(duì)目前流行的技術(shù)不知從何下手胶征,需要突破技術(shù)瓶頸的可以加。

2桨仿、在公司待久了睛低,過得很安逸,但跳槽時(shí)面試碰壁服傍。需要在短時(shí)間內(nèi)進(jìn)修钱雷、跳槽拿高薪的可以加。

3吹零、如果沒有工作經(jīng)驗(yàn)罩抗,但基礎(chǔ)非常扎實(shí),對(duì)java工作機(jī)制灿椅,常用設(shè)計(jì)思想套蒂,常用java開發(fā)框架掌握熟練的,可以加茫蛹。

4操刀、覺得自己很牛B,一般需求都能搞定婴洼。但是所學(xué)的知識(shí)點(diǎn)沒有系統(tǒng)化骨坑,很難在技術(shù)領(lǐng)域繼續(xù)突破的可以加。

5.阿里Java高級(jí)大牛直播講解知識(shí)點(diǎn)窃蹋,分享知識(shí)卡啰,多年工作經(jīng)驗(yàn)的梳理和總結(jié),帶著大家全面警没、科學(xué)地建立自己的技術(shù)體系和技術(shù)認(rèn)知匈辱!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市杀迹,隨后出現(xiàn)的幾起案子亡脸,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浅碾,死亡現(xiàn)場(chǎng)離奇詭異大州,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)垂谢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門厦画,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人滥朱,你說我怎么就攤上這事根暑。” “怎么了徙邻?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵排嫌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我缰犁,道長(zhǎng)淳地,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任帅容,我火速辦了婚禮颇象,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丰嘉。我一直安慰自己夯到,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布饮亏。 她就那樣靜靜地躺著耍贾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪路幸。 梳的紋絲不亂的頭發(fā)上荐开,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音简肴,去河邊找鬼晃听。 笑死,一個(gè)胖子當(dāng)著我的面吹牛砰识,可吹牛的內(nèi)容都是我干的能扒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辫狼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼初斑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膨处,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤见秤,失蹤者是張志新(化名)和其女友劉穎砂竖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹃答,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乎澄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了测摔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片置济。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖避咆,靈堂內(nèi)的尸體忽然破棺而出舟肉,到底是詐尸還是另有隱情,我是刑警寧澤查库,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站黄琼,受9級(jí)特大地震影響樊销,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脏款,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一围苫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撤师,春花似錦剂府、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至痒谴,卻和暖如春衰伯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背积蔚。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工意鲸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尽爆。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓怎顾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親漱贱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子槐雾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文基于周志明的《深入理解java虛擬機(jī) JVM高級(jí)特性與最佳實(shí)踐》所寫。特此推薦饱亿。 線程安全 簡(jiǎn)單定義:如果一個(gè)...
    陽光的技術(shù)小棧閱讀 472評(píng)論 0 5
  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書筆記蚜退,整理的知識(shí)點(diǎn)闰靴,也是為了防止忘記,尊重勞動(dòng)成果钻注,轉(zhuǎn)載注明出處哦蚂且!如果你也喜歡,那...
    波波波先森閱讀 11,268評(píng)論 4 56
  • 進(jìn)程和線程 進(jìn)程 所有運(yùn)行中的任務(wù)通常對(duì)應(yīng)一個(gè)進(jìn)程,當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行時(shí),即變成一個(gè)進(jìn)程.進(jìn)程是處于運(yùn)行過程中...
    勝浩_ae28閱讀 5,110評(píng)論 0 23
  • 等了一冬,總算盼來了穿暖花開捆交。終于可以出去盡量玩耍了淑翼。其實(shí)玩耍倒是其次,各種拍照擺Pose才是重點(diǎn)品追。然而不管風(fēng)景多...
    鯉趣閱讀 413評(píng)論 0 0
  • 文:盼盼 有一句話說玄括,每個(gè)男人恨不得享盡天下美女,而每個(gè)女人恨不得穿盡天下華服肉瓦。 所以我也不例外遭京,每天...
    盼盼00128閱讀 336評(píng)論 0 0