天天看點

多線程面試--進階篇2(Lock和Condition和AQL分析)Lock和Condition和AQL分析

Lock和Condition和AQL分析

1.已經有了synchronized為什麼還要Lock?

1.可以嘗試非阻塞的擷取鎖

2.可以逾時擷取鎖

3.可以在擷取鎖的過程中可以被中斷

2.Lock的常用API

Lock(),

tryLock嘗試非阻塞地擷取鎖

lockInterruptibly:可中斷

unlock()

3.鎖的可重入是什麼?

可重入鎖指的是可重複可遞歸調用的鎖,在外層使用鎖之後,在内層仍然可以使用,并且不發生死鎖(前提得是同一個對象或者class),這樣的鎖就叫做可重入鎖

(如果遞歸的時候,沒有鎖的可重入,就會死鎖)

4.公平和非公平鎖是什麼?

公平鎖,先對鎖發出擷取請求的一定先獲得,(公平鎖的效率比非公平鎖效率要低)。

非公平鎖反之;

5.讀寫鎖ReentrantReadWriteLock有什麼用?

允許多個讀線程同時進行,但是隻允許一個寫線程 (在讀多寫少場景,性能會有提升.)

6.Condition接口是什麼,有何用處?

Condition對象是由Lock對象(調用Lock對象的newCondition()方法)建立出來的,

Condition定義了等待/通知兩種類型的方法,

Condition接口通過和Lock配合來實作等待通知機制

通知方法:

是調用Condition的signal()方法

等待方法是

是調用Condition的await()方法

7.AQS(AbstractQueuedSynchronizer)的基本使用方法

同步器的主要使用方式是繼承,子類通過繼承同步器并實作它的抽象方法來管理同步狀态。

可重寫的方法有:

tryAcquire 獨占鎖擷取

tryRelease 獨占鎖釋放

tryAcquireShared 共享鎖擷取

tryReleaseShared 共享鎖釋放

isHeldExclusively 快速判斷被線程獨占

同步器的設計是基于模闆方法模式

使用者需要繼承同步器并重寫指定的方法,随後将同步器組合在自定義同步元件的實作中,并調用同步器提供的模闆方法,而這些模闆方法将會調用使用者重寫的方法。

對同步狀态進行更改就需要使用同步器提供的3個方法

1.getState() 擷取同步狀态

2.setState 設定同步狀态

3.compareAndSetState 原子的設定同步狀态

8.LockSupport 是什麼

LockSupport是用于提供park/unpark操作的,也是建構同步元件的基礎工具,

LockSupport定義了一組以park開頭的方法用來阻塞目前線程,

以及unpark(Thread thread)方法來喚醒一個被阻塞的線程。

9.同步隊列

1.同步器依賴内部的同步隊列(一個FIFO雙向隊列)來完成同步狀态的管理,

2.目前線程擷取同步狀态失敗時,同步器會将目前線程以及等待狀态等資訊構造成為一個節點(Node)并将其加入同步隊列。

3.同步器擁有首節點(head)和尾節點(tail),沒有成功擷取同步狀态的線程将會成為節點加入該隊列的尾部。

9.獨占式同步狀态擷取與釋放

通過調用同步器的acquire(int arg)方法可以擷取同步狀态

其主要邏輯是:

1.首先調用自定義同步器實作的tryAcquire(int arg)方法,該方法保證線程安全的擷取同步狀态,

2.如果同步狀态擷取失敗,則構造同步節點(獨占式Node.EXCLUSIVE,同一時刻隻能有一個線程成功擷取同步狀态)并通過addWaiter(Node node)方法将該節點加入到同步隊列的尾部,

3.最後調用acquireQueued(Node node,int arg)方法,使得該節點以“死循環”的方式擷取同步狀态。

4.如果擷取不到則阻塞節點中的線程,而被阻塞線程的喚醒主要依靠前驅節點的出隊或阻塞線程被中斷來實作。

10.共享式同步狀态擷取與釋放

共享式擷取與獨占式擷取最主要的差別在于:同一時刻能否有多個線程同時擷取到同步狀态。

在acquireShared(int arg)方法中,同步器調用tryAcquireShared(int arg)方法嘗試擷取同步狀态,

tryAcquireShared(int arg)方法傳回值為int類型,當傳回值大于等于0時,表示能夠擷取到同步狀态。

是以,在共享式擷取的自旋過程中,成功擷取到同步狀态并退出自旋的條件就是tryAcquireShared(int arg)方法傳回值大于等于0。

11.ReentrantLock的實作了解多少

重入鎖ReentrantLock:指的是任意線程在擷取到鎖之後能夠再次擷取該鎖而不會被鎖所阻塞

實作該特性屬性解決2個問題.

一是,當線程再次擷取鎖時,鎖需要去識别擷取鎖的線程是否是目前占據鎖的線程,如果是,則再次成功擷取。

二是,鎖的最終釋放。

nonfairTryAcquire方法裡實作問題一再次擷取同步狀态的處理邏輯:

通過判斷目前線程是否是擷取鎖的線程來決定擷取操作是否成功,

如果是擷取鎖的線程再次請求,則将同步狀态值進行增加并傳回true,

表示擷取同步狀态成功。

同步狀态值表示鎖被一個線程重複擷取的次數。

如果該鎖被擷取了n次,那麼前(n-1)次tryRelease(int releases)方法必須傳回false,

而隻有同步狀态完全釋放了,才能傳回true。

該方法将同步狀态是否為0作為最終釋放的條件,

當同步狀态為0時,将占有線程設定為null,并傳回true,表示釋放成功。

12.對ReentrantReadWriteLock實作的了解

實作讀寫鎖的關鍵是讀寫鎖的自定義同步器在同步狀态上維護多個讀線程和一個寫線程的狀态

如果在一個整型變量上維護多種狀态,就一定需要“按位切割使用”這個變量,

讀寫鎖将變量切分成了兩個部分,高16位表示讀,低16位表示寫。

讀狀态是所有線程擷取讀鎖次數的總和,而每個線程各自擷取讀鎖的次數隻能選擇儲存在ThreadLocal中,由線程自身維護。

13.對Condition實作的了解

等待隊列是一個FIFO的隊列

在隊列中的每個節點都包含了一個線程引用,該線程就是在Condition對象上等待的線程,如果一個線程調用了Condition.await()方法,那麼該線程将會釋放鎖并構造成節點加入等待隊列并進入等待狀态。

一個Condition包含一個等待隊列,新增節點隻需要将原有的尾節點nextWaiter指向它,并且更新尾節點即可。

調用Condition的signal()方法,将會喚醒在等待隊列中等待時間最長的節點(首節點),在喚醒節點之前,會将節點移到同步隊列中。

(調用該方法的前置條件是目前線程必須擷取了鎖,signal()方法會進行isHeldExclusively()檢查,也就是目前線程必須是擷取了鎖的線程。)

接着擷取等待隊列的首節點,将其移動到同步隊列并使用LockSupport喚醒節點中的線程。

通過調用同步器的enq(Node node)方法,使等待隊列中的頭節點線程安全地移動到同步隊列。

(Condition的signalAll()方法,相當于對等待隊列中的每個節點均執行一次signal()方法,效果就是将等待隊列中所有節點全部移動到同步隊列中,并喚醒每個節點的線程。)

繼續閱讀