摘要:當(dāng)前線程在超時(shí)時(shí)間內(nèi)被中斷超時(shí)時(shí)間結(jié)束,返回釋放鎖獲取等待通知組件,該組件和當(dāng)前的鎖綁定,當(dāng)前線程只有獲取了鎖,才能調(diào)用該組件的方法,調(diào)用后,當(dāng)前線程將釋放鎖。同步器是實(shí)現(xiàn)鎖的關(guān)鍵,在鎖的實(shí)現(xiàn)中聚合同步器,利用同步器實(shí)現(xiàn)鎖的語義。
本文在參考java并發(fā)編程實(shí)戰(zhàn)后完成,參考內(nèi)容較多
Java中的鎖鎖是用來控制多線程訪問共享資源的方式,一個(gè)鎖能夠防止多個(gè)線程同事訪問共享資源。在Lock接口出現(xiàn)之前,Java程序是通過synchronized來實(shí)現(xiàn)鎖功能的,在JDK1.5之后,新增的Lock接口可以實(shí)現(xiàn)鎖功能,他的功能與Synchronized類似,但是需要顯式的獲取和釋放鎖,他失去了隱式獲取釋放鎖的便捷性,但是可操作性更強(qiáng),同時(shí)具有可中斷獲取鎖以及超時(shí)獲取鎖的特性。
Lock接口提供了幾個(gè)synchronized不具備的主要特性:
嘗試非阻塞的獲取鎖
能被中斷的獲取鎖
超時(shí)獲取鎖
Lock是一個(gè)接口,定義如下:
package java.util.concurrent.locks; import java.util.concurrent.TimeUnit; /** * @see ReentrantLock * @see Condition * @see ReadWriteLock * * @since 1.5 * @author Doug Lea */ public interface Lock { /** * 線程獲取鎖,如果獲取鎖失敗,線程無法向下執(zhí)行 */ void lock(); /** * 可中端的獲取鎖,和lock()相比這個(gè)方法可以響應(yīng)中斷,就是在獲取鎖的過程中可以中斷當(dāng)前線程 */ void lockInterruptibly() throws InterruptedException; /** * 嘗試非阻塞的獲取鎖,調(diào)用該方法后立刻返回,獲取鎖成功返回true,否則返回false */ boolean tryLock(); /** * 超時(shí)的獲取鎖,在發(fā)生下面的情況下會返回: * 1、在超時(shí)時(shí)間范圍內(nèi)獲取鎖成功立刻返回。2、當(dāng)前線程在超時(shí)時(shí)間內(nèi)被中斷 3、超時(shí)時(shí)間結(jié)束,返回false */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * 釋放鎖 */ void unlock(); /** * 獲取等待通知組件,該組件和當(dāng)前的鎖綁定,當(dāng)前線程只有獲取了鎖,才能調(diào)用該組件的wait方法,調(diào)用后,當(dāng)前線程將釋放鎖。 */ Condition newCondition(); }
隊(duì)列同步器AbstractQueuedSynchronizer,是構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架,他使用int成員變量表示同步狀態(tài)(這個(gè)同步狀態(tài)在不同的同步組件中表示的含義會有差異),通過內(nèi)置的FIFO隊(duì)列完成線程獲取資源的排隊(duì)工作。
同步器使用的主要方式是通過繼承并實(shí)現(xiàn)它定義的抽象方法來管理同步狀態(tài)。隊(duì)列同步器提供了操作同步狀態(tài)的方法,可以保證狀態(tài)的修改是線程安全的,同步器支持獨(dú)占的獲取同步狀態(tài),也支持共享式的獲取。
同步器是實(shí)現(xiàn)鎖的關(guān)鍵,在鎖的實(shí)現(xiàn)中聚合同步器,利用同步器實(shí)現(xiàn)鎖的語義。鎖是面向使用這的,他定義了使用者與鎖交互的接口,隱藏了實(shí)現(xiàn)細(xì)節(jié);同步器面向的是鎖的實(shí)現(xiàn)者,他簡化了鎖的實(shí)現(xiàn)方式,屏蔽了同步狀態(tài)管理、線程排隊(duì),等待喚醒等底層操作。
AbstractQueuedSynchronizer的定義:
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable
//同步器提供的修改活訪問狀態(tài)的方法
//獲取當(dāng)前同步狀態(tài) protected final int getState() { return state; } //設(shè)置當(dāng)前同步狀態(tài) protected final void setState(int newState) { state = newState; } //使用CAS設(shè)置當(dāng)前狀態(tài),可以保證設(shè)置狀態(tài)的原子性 protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
同步器的設(shè)計(jì)是基于模版方法模式的,使用者需要繼承同步器并重寫指定的方法,隨后將同步器組合在自定的同步組件實(shí)現(xiàn)中,并調(diào)用同步器提供的模版方法,而這些模版方法將會調(diào)用使用者重寫的方法
ReentrantLock是Java中提供的另一種鎖的實(shí)現(xiàn).他通過調(diào)用lock()方法獲取鎖;調(diào)用unlock()方法釋放鎖。
ReentrantLock的實(shí)現(xiàn)依賴于Java同步框架AbstractQueuedSynchronizer,AQS使用一個(gè)整形的volatile變量(命名為state)來維護(hù)同步狀態(tài),volatile變量是ReentrantLock內(nèi)存語義實(shí)現(xiàn)的關(guān)鍵。
公平鎖:每個(gè)線程搶占鎖的順序?yàn)橄群笳{(diào)用lock方法的順序依次獲取鎖
每個(gè)線程搶占鎖的順序不定,誰運(yùn)氣好,誰就獲取到鎖,和調(diào)用lock方法的先后順序無關(guān)。
ReentrantLock的類圖如下:
ReentrantLock實(shí)現(xiàn)了Lock接口,內(nèi)部有三個(gè)內(nèi)部類,Sync、NonfairSync、FairSync,Sync是一個(gè)抽象類型,它繼承AbstractQueuedSynchronizer,這個(gè)AbstractQueuedSynchronizer是一個(gè)模板類,它實(shí)現(xiàn)了許多和鎖相關(guān)的功能,并提供了鉤子方法供用戶實(shí)現(xiàn),比如tryAcquire,tryRelease等。Sync實(shí)現(xiàn)了AbstractQueuedSynchronizer的tryRelease方法。NonfairSync和FairSync兩個(gè)類繼承自Sync,實(shí)現(xiàn)了lock方法,然后分別公平搶占和非公平搶占針對tryAcquire有不同的實(shí)現(xiàn)。
首先分析公平鎖:
ReentrantLock lock = new ReentrantLock(true);公平鎖聲明,如果不為true,或者使用默認(rèn),那么是非公平鎖。
加鎖lock()方法的調(diào)用軌跡如下:
ReentrantLock.lock()
sync.lock() [ReentrantLock.FairSync.lock()]
AbstractQueuedSynchronizer.acquire(int arg) [arg = 1]
ReentrantLock.FairSync.tryAcquire(int acquires);
第4步是加鎖的關(guān)鍵步驟:
protected final boolean tryAcquire(int acquires) { //獲取當(dāng)前嘗試獲取鎖的線程 final Thread current = Thread.currentThread(); //獲取AQS中的volatile變量state int c = getState(); if (c == 0) { //判斷隊(duì)列之前是否有其他的線程在等待 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { //設(shè)置鎖的持有者為當(dāng)前線程 setExclusiveOwnerThread(current); return true; } } //如果當(dāng)前線程已經(jīng)獲取了鎖,那么進(jìn)行鎖重入 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
CAS + volatile 構(gòu)成了AQS以及原子變量類。
JDK文檔對CAS的說明:如果當(dāng)前狀態(tài)值與預(yù)期值相等,則以原子的方式講同步狀態(tài)設(shè)置為給定的更新值,此操作具有volatile讀和寫的內(nèi)存語義。這意味著編譯器不能對CAS與CAS前面和后面的任意內(nèi)存操作重排序。CAS操作對應(yīng)的本地方法最終對應(yīng)的處理器源代碼回有一個(gè)Atomic::cmpxchg指令。程序回根據(jù)當(dāng)前處理器的類型來決定是否為cmpxchg指令添加lock前綴。如果是多處理器上運(yùn)行,那么添加Lock前綴,如果是單處理器那么就會省略。lock前綴會確保內(nèi)存的讀寫改操作原子執(zhí)行。(但處理器自身會維護(hù))
【補(bǔ)充volatile的內(nèi)存語義:1、在程序中,當(dāng)?shù)谝粋€(gè)操作為普通變量的讀或?qū)憰r(shí),如果第二個(gè)操作為volatile寫,則編譯器不能重拍下這兩個(gè)操作 2、當(dāng)?shù)诙€(gè)操作為volatile寫時(shí),不管第一個(gè)操作是什么,都不能重排序。這個(gè)規(guī)則確保volatile寫之前的操作不會被編譯器重排序到volatile寫之后 3、當(dāng)?shù)谝粋€(gè)操作是volatile讀時(shí),不管第二個(gè)操作是什么,都不能重排序,這個(gè)規(guī)則確保volatile讀之后的操作不會被編譯器重排序到volatile讀之前 4、當(dāng)?shù)谝粋€(gè)操作是volatile寫,第二個(gè)操作是volatile讀時(shí),不能重排序】
ReentrantLock是支持可重入的排他鎖,這些鎖在同一時(shí)刻只允許一個(gè)線程進(jìn)行訪問,而讀寫鎖在同一時(shí)刻可以允許多個(gè)讀線程訪問,但是在寫線程訪問時(shí),所有和讀線程和其他的寫線程均被阻塞。讀寫鎖維護(hù)了兩個(gè)鎖,一個(gè)讀鎖,一個(gè)寫鎖,通過讀寫鎖,可以提高兵法性能。
Java中提供的讀寫鎖的實(shí)現(xiàn)是ReentrantReadWriteLock,提供的特性如下:
公平性選擇,支持公平和非公平的鎖獲取方式
重進(jìn)入
鎖降級 遵循獲取寫鎖,獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成讀鎖。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/74594.html
摘要:死亡狀態(tài)線程退出有可能是正常執(zhí)行完成也有可能遇見異常退出。類有新建與死亡狀態(tài)返回其余狀態(tài)返回判斷線程是否存活。線程因某些原因進(jìn)入阻塞狀態(tài)。執(zhí)行同步代碼塊的過程中執(zhí)行了當(dāng)前線程放棄開始睡眠進(jìn)入就緒狀態(tài)但是不會釋放鎖。 【java內(nèi)存模型簡介 JVM中存在一個(gè)主存區(qū)(Main Memory或Java Heap Memory),Java中所有變量都是存在主存中的,對于所有線程進(jìn)行共享,而每個(gè)...
摘要:解決上問題在變量前添加版本號,將變成循環(huán)時(shí)間長開銷大,因?yàn)樽孕枰闹荒鼙WC一個(gè)共享變量的原子操作分類二重入鎖支持重進(jìn)入的鎖,排它鎖分類三讀寫鎖一對鎖,讀鎖,寫鎖,在同一時(shí)刻允許多線程訪問 1、 分類一:樂觀鎖與悲觀鎖 a)悲觀鎖:認(rèn)為其他線程會干擾本身線程操作,所以加鎖 i.具體表現(xiàn)形式:synchronized關(guān)鍵字和lock實(shí)現(xiàn)類 ...
摘要:第一個(gè)字被稱為。經(jīng)量級鎖的加鎖過程當(dāng)一個(gè)對象被鎖定時(shí),被復(fù)制到當(dāng)前嘗試獲取鎖的線程的線程棧的鎖記錄空間被復(fù)制的官方稱為。根據(jù)鎖對象目前是否處于被鎖定狀態(tài),撤銷偏向后恢復(fù)到未鎖定或經(jīng)量級鎖定狀態(tài)。 Synchronized關(guān)鍵字 synchronized的鎖機(jī)制的主要優(yōu)勢是Java語言內(nèi)置的鎖機(jī)制,因此,JVM可以自由的優(yōu)化而不影響已存在的代碼。 任何對象都擁有對象頭這一數(shù)據(jù)結(jié)構(gòu)來支持鎖...
摘要:限期阻塞調(diào)用方法等待時(shí)間結(jié)束或線程執(zhí)行完畢。終止?fàn)顟B(tài)線程執(zhí)行完畢或出現(xiàn)異常退了。和都會檢查線程何時(shí)中斷,并且在發(fā)現(xiàn)中斷時(shí)提前放回。工廠方法將線程池的最大大小設(shè)置為,而將基本大小設(shè)置為,并將超時(shí)大小設(shè)置為分鐘。 wait()、notify()、notifyAll() Object是所有類的基類,它有5個(gè)方法組成了等待、通知機(jī)制的核心:notify()、notifyAll()、wait()...
閱讀 2993·2023-04-26 02:25
閱讀 2265·2023-04-25 18:05
閱讀 659·2021-09-30 09:57
閱讀 2952·2021-09-27 14:10
閱讀 1665·2019-08-30 15:44
閱讀 1014·2019-08-29 15:28
閱讀 2540·2019-08-29 14:10
閱讀 2270·2019-08-29 13:30