在上篇博客(【Java並發編程實戰】----- AQS(二):獲取鎖、釋放鎖)中提到,當一個線程加入到CLH隊列中時,如果不是頭節點是需要判斷該節點是否需要掛起;在釋放鎖后,需要喚醒該線程的繼任節點
lock方法,在調用acquireQueued():
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true;
在acquireQueued()中調用parkAndCheckInterrupt()來掛起當前線程:
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
調用LockSupport.park()方法。對於park():為了線程調度,在許可可用之前禁用當前線程。
釋放鎖后,需要喚醒該線程繼任節點:
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
在release方法中調用unparkSuccessor()來喚醒該線程的繼任節點。在unparkSuccessor()方法中通過LockSupport.unpark()來喚醒。unpark():如果給定線程的許可尚不可用,則使其可用。
LockSupport
LockSupport是用來創建鎖和其他同步類的基本線程阻塞原語。每個使用LockSupport的線程都會與一個許可關聯,如果該許可可用,並且可在進程中使用,則調用park()將會立即返回,否則可能阻塞。如果許可尚不可用,則可以調用 unpark
使其可用。但是注意許可不可重入,也就是說只能調用一次park()方法,否則會一直阻塞。
LockSupport.park()、LockSupport.unpark()的作用分別是阻塞線程和解除阻塞線程,且park()和unpark()不會遇到“Thread.suspend ()和 Thread.resume所可能引發的死鎖”問題。當然park()、unpark()是成對使用。
park():如果許可可用,則使用該許可,並且該調用立即返回;否則,為線程調度禁用當前線程,並在發生以下三種情況之一以前,使其處於休眠狀態:
unpark
;或者 其源碼實現如下:
public static void park() { unsafe.park(false, 0L); }
unpark:如果給定線程的許可尚不可用,則使其可用。如果線程在 park 上受阻塞,則它將解除其阻塞狀態。否則,保證下一次調用 park 不會受阻塞。如果給定線程尚未啟動,則無法保證此操作有任何效果。
其源代碼如下:
public static void unpark(Thread thread) { if (thread != null) { Object lock = unsafe.getObject(thread, lockOffset); synchronized (lock) { if (thread.isAlive()) { unsafe.unpark(thread); } } } }
一般來說park()、unpark()是成對出現的,同時unpark必須要在park執行之后執行,當然並不是說沒有不調用unpark線程就會一直阻塞,park有一個方法,它帶了時間戳(parkNanos(long nanos):為了線程調度禁用當前線程,最多等待指定的等待時間,除非許可可用。)
參考資料