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