ReentrantLock的加鎖方法Lock()提供了無條件地輪詢獲取鎖的方式,lockInterruptibly()提供了可中斷的鎖獲取方式。這兩個方法的區(qū)別在哪里呢?通過分析源碼可以知道lock方法默認(rèn)處理了中斷請求,一旦監(jiān)測到中斷狀態(tài),則中斷當(dāng)前線程;而lockInterruptibly()則直接拋出中斷異常,由上層調(diào)用者區(qū)去處理中斷。
? ? ? ?? lock獲取鎖過程中,忽略了中斷,在成功獲取鎖之后,再根據(jù)中斷標(biāo)識處理中斷,即selfInterrupt中斷自己。 acquire操作源碼如下:
/**
??*默認(rèn)處理中斷方式是selfInterrupt
?*/??
public?final?void?acquire(int?arg)?{??
if?(!tryAcquire(arg)?&&??
????????acquireQueued(addWaiter(Node.EXCLUSIVE),?arg))??
????????selfInterrupt();??
}??
? ? ? acquireQueued,在for循環(huán)中無條件重試獲取鎖,直到成功獲取鎖,同時返回線程中斷狀態(tài)。該方法通過for循正常返回時,必定是成功獲取到了鎖。
/**
?*無條件重試,直到成功返回,并且記錄中斷狀態(tài)
?*/??
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);??
????}??
}??
? ? ?可中斷加鎖,即在鎖獲取過程中不處理中斷狀態(tài),而是直接拋出中斷異常,由上層調(diào)用者處理中斷。源碼細(xì)微差別在于鎖獲取這部分代碼,這個方法與acquireQueue差別在于方法的返回途徑有兩種,一種是for循環(huán)結(jié)束,正常獲取到鎖;另一種是線程被喚醒后檢測到中斷請求,則立即拋出中斷異常,該操作導(dǎo)致方法結(jié)束。
/**
?????*?Acquires?in?exclusive?interruptible?mode.
?????*?@param?arg?the?acquire?argument
?????*/??
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);??
????????}??
????}??
? ? ?結(jié)論:ReentrantLock的中斷和非中斷加鎖模式的區(qū)別在于:線程嘗試獲取鎖操作失敗后,在等待過程中,如果該線程被其他線程中斷了,它是如何響應(yīng)中斷請求的。lock方法會忽略中斷請求,繼續(xù)獲取鎖直到成功;而lockInterruptibly則直接拋出中斷異常來立即響應(yīng)中斷,由上層調(diào)用者處理中斷。
? ? ?那么,為什么要分為這兩種模式呢?這兩種加鎖方式分別適用于什么場合呢?根據(jù)它們的實現(xiàn)語義來理解,我認(rèn)為lock()適用于鎖獲取操作不受中斷影響的情況,此時可以忽略中斷請求正常執(zhí)行加鎖操作,因為該操作僅僅記錄了中斷狀態(tài)(通過Thread.currentThread().interrupt()操作,只是恢復(fù)了中斷狀態(tài)為true,并沒有對中斷進(jìn)行響應(yīng))。如果要求被中斷線程不能參與鎖的競爭操作,則此時應(yīng)該使用lockInterruptibly方法,一旦檢測到中斷請求,立即返回不再參與鎖的競爭并且取消鎖獲取操作(即finally中的cancelAcquire操作)