在上文講到,AQS中需要支持阻塞與釋放兩類操作,那么最重要的自然是acquire和release了。
對于acquire,內部會調用tryAcquire()方法,而tryAcquire是一個protected的方法,在本抽象類中并未給出實現。因此這篇文中主要會討論除調用tryAcquire方法外還需要做哪些操作。具體的tryAcquire后面也會繼續探究。
我們先來說一下acquire方法的思路,首先tryAcquire方法如果成功,那沒的說直接返回true就好。如果失敗了,那么需要再隊列中插入一個新節點以表示當前線程。加入隊列后我們來看他是否加在頭結點的后面,如果是那么他可以立即嘗試一波tryAcquire。這次try中如果他成功了,那么當前節點就可以作為頭結點了(同時釋放前頭結點)。如果前驅不是頭結點或者try失敗了,那么就要考慮是不是需要阻塞線程。
如何判斷呢?那就是如果前驅節點的waitState是SIGNAL,那么意味著后繼節點正在或者將要阻塞,那么當前線程可以安全的阻塞。如果waitState大于0,那么這個前驅代表的是一個被取消的線程,那我們就接著向前找,直到找到一個有效節點,此時需要重新判斷其前驅是不是頭結點,后續如上所述。那如果小于0,就需要用CAS操作設置前驅狀態為SIGNAL表示自己將處于阻塞狀態,然后再重新檢查前驅是不是頭結點重新try一下什么的,也是之前描述的流程。為了更好地理解,可以參照下面的圖示:
然后我們再來看一下acquireInterruptibly方法,該方法無非多了一個中斷則取消請求,因此在方法流程上并沒有什么區別:
可以看到,在acquire中,如果park操作被中斷,那么只是記錄了interrupted狀態,然后繼續進入循環判斷是否可以acquire或者阻塞。而在acquireInterruptibly中,一旦被中斷,那么就立即拋出InterruptedException異常。其他方面兩個方法并沒有顯著不同。
接下來是tryAcquireNanos方法。可以被中斷,增加了超時則失敗的功能。可以說該方法的實現與上述兩方法沒有任何區別。那時間功能是如何實現的呢?
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
如果剩余時間小于0那么acquire失敗,如果該時間大于一次自旋鎖時間(1000L),并且可以被阻塞,那么調用LockSupport.parkNanos方法阻塞線程。
對于共享模式下的acquire,其區別主要是使用了tryAcquireShared,該方法是一個需要子類實現的方法。
那么對于acquire的方法實現這一章就講完了。