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

多線程三個特征:原子性、可見性以及有序性.

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

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

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

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

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

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

2.互斥:對于線程A和線程B來講,在同一時刻,只允許一個線程對臨界資源進行操作,即當A進入臨界區(qū)對資源操作時,B就必須等待;當A執(zhí)行完,退出臨界區(qū)后,B才能對臨界資源進行操作。

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

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

互斥是通過競爭對資源的獨占使用,彼此之間不需要知道對方的存在,執(zhí)行順序是一個亂序。

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

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

public class StaticSynDemo {

private static String a="test";

public void print2(String b){

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

System.out.println(b+a);

}

}

public?static?void print4(String b){

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

System.out.println(b+a);

}

}

}

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

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

this關(guān)鍵字代表類的一個對象,所以其內(nèi)存鎖是針對相同對象的互斥操作,而static成員屬于類專有,其內(nèi)存空間為該類所有成員共有,這就導致synchronized()對static成員加鎖,相當于對類加鎖,也就是在該類的所有成員間實現(xiàn)互斥,在同一時間只有一個線程可訪問該類的實例。

Synchronized和Static Synchronized區(qū)別:

一個是實例鎖(鎖在某一個實例對象上,如果該類是單例,那么該鎖也具有全局鎖的概念),一個是全局鎖(該鎖針對的是類,無論實例多少個對象,那么線程都共享該鎖)。

實例鎖對應的就是synchronized關(guān)鍵字,而類鎖(全局鎖)對應的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。

synchronized(this)與synchronized(static XXX)的區(qū)別了,synchronized就是針對內(nèi)存區(qū)塊申請內(nèi)存鎖,this關(guān)鍵字代表類的一個對象,所以其內(nèi)存鎖是針對相同對象的互斥操作,而static成員屬于類專有,其內(nèi)存空間為該類所有成員共有,這就導致synchronized()對static成員加鎖,相當于對類加鎖,也就是在該類的所有成員間實現(xiàn)互斥,在同一時間只有一個線程可訪問該類的實例。

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

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

1.對變量的寫操作不依賴于當前值。

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

> 線程安全

造成線程安全問題的主要誘因有兩點:

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

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

在單線程中不會出現(xiàn)線程安全問題,而在多線程編程中,有可能會出現(xiàn)同時訪問同一個資源的情況,這種資源可以是各種類型的的資源:一個變量、一個對象、一個文件、一個數(shù)據(jù)庫表等,而當多個線程同時訪問同一個資源的時候,就會存在一個問題:

由于每個線程執(zhí)行的過程是不可控的,所以很可能導致最終的結(jié)果與實際上的愿望相違背或者直接導致程序出錯。線程安全問題,即多個線程同時訪問一個資源時,會導致程序運行結(jié)果并不是想看到的結(jié)果。這里面,這個資源被稱為:臨界資源(也有稱為共享資源)。

也就是說,當多個線程同時訪問臨界資源(一個對象,對象中的屬性,一個文件,一個數(shù)據(jù)庫等)時,就可能會產(chǎn)生線程安全問題。不過,當多個線程執(zhí)行一個方法,方法內(nèi)部的局部變量并不是臨界資源,因為方法是在棧上執(zhí)行的,而Java棧是線程私有的,因此不會產(chǎn)生線程安全問題。

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

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

在了解synchronized關(guān)鍵字的使用方法之前,我們先來看一個概念:互斥鎖,顧名思義:能到達到互斥訪問目的的鎖。舉個簡單的例子:如果對臨界資源加上互斥鎖,當一個線程在訪問該臨界資源時,其他線程便只能等待。

在Java中,每一個對象都擁有一個鎖標記(monitor),也稱為監(jiān)視器,多線程同時訪問某個對象時,線程只有獲取了該對象的鎖才能訪問。

在Java中,可以使用synchronized關(guān)鍵字來標記一個方法或者代碼塊,當某個線程調(diào)用該對象的synchronized方法或者訪問synchronized代碼塊時,這個線程便獲得了該對象的鎖,其他線程暫時無法訪問這個方法,只有等待這個方法執(zhí)行完畢或者代碼塊執(zhí)行完畢,這個線程才會釋放該對象的鎖,其他線程才能執(zhí)行這個方法或者代碼塊。

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

互斥鎖的本質(zhì): 首先需要明確一點,互斥鎖實際上是一種變量,在使用互斥鎖時,實際上是對這個變量進行置0置1操作并進行判斷使得線程能夠獲得鎖或釋放鎖。

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

1、互斥屬性:即每次只能有一個線程占用資源。

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

3、不可剝奪:線程已經(jīng)得到所資源,在沒有自己主動釋放之前,不能被強行剝奪。

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

-- 死鎖的避免:?

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

2、盡量避免同時獲得多把鎖,如果有必要,就要保證獲得鎖的順序相同。

> Java鎖:無鎖狀態(tài)、偏向鎖、輕量級鎖和重量級鎖

-- 鎖的使用場景?

由于偏向鎖在線程存在競爭的時候會帶來額外的性能開銷,所以偏向鎖適用于只有一個線程方法同步快的情況;輕量級鎖在線程競爭鎖的情況下不會導致線程阻塞,但是會通過自旋消耗CPU,所以輕量級鎖適用于追求響應時間的情況。重量級鎖線程競爭不會使用自旋,但是線程競爭會導致阻塞,所以響應時間比較慢,重量級鎖一般使用在追求吞吐量的情況。

-- 當前常用的多線程同步機制可以分為下面三種類型:?

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

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

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

?-- java同步機制: volatile、synchronized和final.

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

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

鎖提供了兩種主要特性:互斥(mutual exclusion) 和可見性(visibility)?;コ饧匆淮沃辉试S一個線程持有某個特定的鎖,因此可使用該特性實現(xiàn)對共享數(shù)據(jù)的協(xié)調(diào)訪問協(xié)議,這樣,一次就只有一個線程能夠使用該共享數(shù)據(jù)??梢娦砸訌碗s一些,它必須確保釋放鎖之前對共享數(shù)據(jù)做出的更改對于隨后獲得該鎖的另一個線程是可見的 —— 如果沒有同步機制提供的這種可見性保證,線程看到的共享變量可能是修改前的值或不一致的值,這將引發(fā)許多嚴重問題。

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

鎖的狀態(tài)總共有四種,無鎖狀態(tài)、偏向鎖、輕量級鎖和重量級鎖。隨著鎖的競爭,鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖,但是鎖的升級是單向的,也就是說只能從低到高升級,不會出現(xiàn)鎖的降級.讀寫鎖特點:

1)多個讀者可以同時進行讀

2)寫者必須互斥(只允許一個寫者寫,也不能讀者寫者同時進行)

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

互斥鎖特點:一次只能一個線程擁有互斥鎖,其他線程只有等待

-- 使用同步機制獲取互斥鎖的情況,進行幾點說明:

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

2、類的每個實例都有自己的對象級別鎖。當一個線程訪問實例對象中的synchronized同步代碼塊或同步方法時,該線程便獲取了該實例的對象級別鎖,其他線程這時如果要訪問synchronized同步代碼塊或同步方法,便需要阻塞等待,直到前面的線程從同步代碼塊或方法中退出,釋放掉了該對象級別鎖。

3、訪問同一個類的不同實例對象中的同步代碼塊,不存在阻塞等待獲取對象鎖的問題,因為它們獲取的是各自實例的對象級別鎖,相互之間沒有影響。

4、持有一個對象級別鎖不會阻止該線程被交換出來,也不會阻塞其他線程訪問同一示例對象中的非synchronized代碼。當一個線程A持有一個對象級別鎖(即進入了synchronized修飾的代碼塊或方法中)時,線程也有可能被交換出去,此時線程B有可能獲取執(zhí)行該對象中代碼的時間,但它只能執(zhí)行非同步代碼(沒有用synchronized修飾),當執(zhí)行到同步代碼時,便會被阻塞,此時可能線程規(guī)劃器又讓A線程運行,A線程繼續(xù)持有對象級別鎖,當A線程退出同步代碼時(即釋放了對象級別鎖),如果B線程此時再運行,便會獲得該對象級別鎖,從而執(zhí)行synchronized中的代碼。

5、持有對象級別鎖的線程會讓其他線程阻塞在所有的synchronized代碼外。例如,在一個類中有三個synchronized方法a,b,c,當線程A正在執(zhí)行一個實例對象M中的方法a時,它便獲得了該對象級別鎖,那么其他的線程在執(zhí)行同一實例對象(即對象M)中的代碼時,便會在所有的synchronized方法處阻塞,即在方法a,b,c處都要被阻塞,等線程A釋放掉對象級別鎖時,其他的線程才可以去執(zhí)行方法a,b或者c中的代碼,從而獲得該對象級別鎖。

6、使用synchronized(obj)同步語句塊,可以獲取指定對象上的對象級別鎖。obj為對象的引用,如果獲取了obj對象上的對象級別鎖,在并發(fā)訪問obj對象時時,便會在其synchronized代碼處阻塞等待,直到獲取到該obj對象的對象級別鎖。當obj為this時,便是獲取當前對象的對象級別鎖。

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

8、互斥是實現(xiàn)同步的一種手段,臨界區(qū)、互斥量和信號量都是主要的互斥實現(xiàn)方式。synchronized關(guān)鍵字經(jīng)過編譯后,會在同步塊的前后分別形成monitorenter和monitorexit這兩個字節(jié)碼指令。根據(jù)虛擬機規(guī)范的要求,在執(zhí)行monitorenter指令時,首先要嘗試獲取對象的鎖,如果獲得了鎖,把鎖的計數(shù)器加1,相應地,在執(zhí)行monitorexit指令時會將鎖計數(shù)器減1,當計數(shù)器為0時,鎖便被釋放了。由于synchronized同步塊對同一個線程是可重入的,因此一個線程可以多次獲得同一個對象的互斥鎖,同樣,要釋放相應次數(shù)的該互斥鎖,才能最終釋放掉該鎖。

-- 無鎖、偏向鎖、輕量級鎖和重量級鎖:

在JDK 1.6中引入了“偏向鎖”和“輕量級鎖“。鎖一共有四種狀態(tài):無鎖、偏向鎖、輕量級鎖和重量級鎖。鎖只能升級,不能降級。當對鎖的競爭加劇的時候,鎖會發(fā)生升級。

1.偏向鎖

之所以引入偏向鎖,是為了讓線程獲得鎖的代價更低。當一個線程訪問同步塊并獲取鎖的時候,會在對象的對象頭(對象頭包括兩部分的信息:一部分是”Mark Word“,主要存放的是哈希碼、對象的分代年齡、鎖的標記等信息;另一部分是對象的類型指針)和棧幀中的鎖記錄中存儲鎖偏向的ID,以后該線程在進入方法的同步塊的時候,就檢查這個ID(可以理解為一種標記,是一種身份的標識),如果測試成功,表明對象已經(jīng)獲得了鎖;如果測試失敗,繼續(xù)測試偏向鎖的標識是否設(shè)置為1(1的話就是偏向鎖),如果沒有則使用CAS(Compare And Swap)鎖。

2.輕量級鎖

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

這里多次提到CAS,那么CAS是個什么鬼?CAS是Compare and swap(比較和替換)的簡寫,具體而言就是:當進行CAS操作的時候,需要輸入兩個數(shù)值,一個是舊值,該舊值是原來的值,另一個是新值,也就是發(fā)生改變的值,得到這兩個值后,在CAS操作期間會去比較舊值是否發(fā)生變化,如果沒有發(fā)生變化就用新值進行替換,如果發(fā)生了變化就不進行替換。

那么解鎖的過程又是怎樣的呢?就是使用CAS操作將Displaced Mark Word替換回對象頭,如果成功,則表示沒有競爭發(fā)生。如果失敗,表示當前鎖存在競爭,鎖就會膨脹,膨脹的結(jié)果是導致鎖的升級,并進入阻塞狀態(tài)。直到需要釋放鎖的線程釋放鎖并喚醒其他等待的線程。

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

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

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

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

可重入代碼又稱為“純代碼”,是一種允許多個進程訪問的代碼,因此,可重入代碼是一種不允許任何進程對它進行修改的代碼 。為了能修改,訪問純代碼的

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

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

并發(fā)編程都是離不開 同步的,而同步最簡單的莫過于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);

若一個程序或子程序可以安全的被并行執(zhí)行,則稱其為可重入(reentrant或re-entrant)的;即,當該子程序正在運行時,可以再次進入并執(zhí)行它。若一個函數(shù)是可重入的,則該函數(shù):

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

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

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

不能依賴于單實例模式資源的鎖。

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

多用戶/對象/進程優(yōu)先級'以及多進程一般會使得對可重入代碼的控制變得復雜。同時,IO代碼通常不是可重入的,因為他們依賴于像磁盤這樣共享的、單獨的資源。

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

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

AQS,AbstractQueuedSynchronizer又稱為隊列同步器(后面簡稱AQS),它是用來構(gòu)建鎖或其他同步組件的基礎(chǔ)框架,內(nèi)部通過一個int類型的成員變量state來控制同步狀態(tài),當state=0時,則說明沒有任何線程占有共享資源的鎖,當state=1時,則說明有線程目前正在使用共享變量,其他線程必須加入同步隊列進行等待,AQS內(nèi)部通過內(nèi)部類Node構(gòu)成FIFO的同步隊列來完成線程獲取鎖的排隊工作,同時利用內(nèi)部類ConditionObject構(gòu)建等待隊列,當Condition調(diào)用wait()方法后,線程將會加入等待隊列中,而當Condition調(diào)用signal()方法后,線程將從等待隊列轉(zhuǎn)移動同步隊列中進行鎖競爭。注意這里涉及到兩種隊列,一種的同步隊列,當線程請求鎖而等待的后將加入同步隊列等待,而另一種則是等待隊列(可有多個),通過Condition調(diào)用await()方法釋放鎖后,將加入等待隊列。

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

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


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

注:加群要求

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

2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內(nèi)進修、跳槽拿高薪的可以加。

3、如果沒有工作經(jīng)驗,但基礎(chǔ)非常扎實,對java工作機制,常用設(shè)計思想,常用java開發(fā)框架掌握熟練的,可以加。

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

5.阿里Java高級大牛直播講解知識點,分享知識,多年工作經(jīng)驗的梳理和總結(jié),帶著大家全面、科學地建立自己的技術(shù)體系和技術(shù)認知!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容