ReentrantLock的加鎖方法Lock()提供了無條件地輪詢擷取鎖的方式,lockInterruptibly()提供了可中斷的鎖擷取方式。這兩個方法的差別在哪裡呢?通過分析源碼可以知道lock方法預設處理了中斷請求,一旦監測到中斷狀态,則中斷目前線程;而lockInterruptibly()則直接抛出中斷異常,由上層調用者區去進行中斷。
1 lock操作
lock擷取鎖過程中,忽略了中斷,在成功擷取鎖之後,再根據中斷辨別進行中斷,即selfInterrupt中斷自己。 acquire操作源碼如下:
[java] view plain copy
- public final void acquire(int arg) {
- if (!tryAcquire(arg) &&
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- selfInterrupt();
- }
acquireQueued,在for循環中無條件重試擷取鎖,直到成功擷取鎖,同時傳回線程中斷狀态。該方法通過for循正常傳回時,必定是成功擷取到了鎖。
[java] view plain copy
- final boolean acquireQueued(final Node node, int arg) {
- boolean failed = true;
- try {
- boolean interrupted = false;
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- failed = false;
- return interrupted;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- interrupted = true;
- }
- } finally {
- if (failed)
- cancelAcquire(node);
- }
- }
2 lockInterruptibly操作
可中斷加鎖,即在鎖擷取過程中不進行中斷狀态,而是直接抛出中斷異常,由上層調用者進行中斷。源碼細微差别在于鎖擷取這部分代碼,這個方法與acquireQueue差别在于方法的傳回途徑有兩種,一種是for循環結束,正常擷取到鎖;另一種是線程被喚醒後檢測到中斷請求,則立即抛出中斷異常,該操作導緻方法結束。
[java] view plain copy
- private void doAcquireInterruptibly(int arg)
- throws InterruptedException {
- final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- failed = false;
- return;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- throw new InterruptedException();
- }
- } finally {
- if (failed)
- cancelAcquire(node);
- }
- }
結論:ReentrantLock的中斷和非中斷加鎖模式的差別在于:線程嘗試擷取鎖操作失敗後,在等待過程中,如果該線程被其他線程中斷了,它是如何響應中斷請求的。lock方法會忽略中斷請求,繼續擷取鎖直到成功;而lockInterruptibly則直接抛出中斷異常來立即響應中斷,由上層調用者進行中斷。
那麼,為什麼要分為這兩種模式呢?這兩種加鎖方式分别适用于什麼場合呢?根據它們的實作語義來了解,我認為lock()适用于鎖擷取操作不受中斷影響的情況,此時可以忽略中斷請求正常執行加鎖操作,因為該操作僅僅記錄了中斷狀态(通過Thread.currentThread().interrupt()操作,隻是恢複了中斷狀态為true,并沒有對中斷進行響應)。如果要求被中斷線程不能參與鎖的競争操作,則此時應該使用lockInterruptibly方法,一旦檢測到中斷請求,立即傳回不再參與鎖的競争并且取消鎖擷取操作(即finally中的cancelAcquire操作)。