whitesock

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            10 Posts :: 0 Stories :: 0 Comments :: 0 Trackbacks

          Inside AbstractQueuedSynchronizer (1)

          Inside AbstractQueuedSynchronizer (2)

          Inside AbstractQueuedSynchronizer (3)

          Inside AbstractQueuedSynchronizer (4)

          3.4 Template Method

          AbstractQueuedSynchronizer提供了以下幾個protected方法用于子類改寫

          protected boolean tryAcquire(int arg)
          protected boolean tryRelease(int arg)
          protected int tryAcquireShared(int arg)
          protected boolean tryReleaseShared(int arg)
          protected boolean isHeldExclusively()
          

          這幾個方法的默認實現是拋出UnsupportedOperationException,子類可以根據需要進行改寫。

          AbstractQueuedSynchronizer中最基本的acquire流程的相關代碼如下:

          public final void acquire(int arg) {
              if (!tryAcquire(arg) &&
                  acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                  selfInterrupt();
          }
          
          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);
              }
          }

          如果tryAcquire失敗,那么當前線程可能會被enqueue到WaitQueue,然后被阻塞。 shouldParkAfterFailedAcquire方法會確保每個線程在被阻塞之前,其對應WaitQueue中的節點的waitStatus被設置為Node.SIGNAL(-1),以便在release時避免不必要的unpark操作。此外shouldParkAfterFailedAcquire還會清理WaitQueue中已經超時或者取消的Node。需要注意的是,在某個線程最終被阻塞之前,tryAcquire可能會被多次調用。

          AbstractQueuedSynchronizer中最基本的release流程的相關代碼如下:

          public final boolean release(int arg) {
              if (tryRelease(arg)) {
                  Node h = head;
                  if (h != null && h.waitStatus != 0)
                      unparkSuccessor(h);
                  return true;
              }
              return false;
          }
          
          private void unparkSuccessor(Node node) {
              /*
               * If status is negative (i.e., possibly needing signal) try
               * to clear in anticipation of signalling.  It is OK if this
               * fails or if status is changed by waiting thread.
               */
              int ws = node.waitStatus;
              if (ws < 0)
                  compareAndSetWaitStatus(node, ws, 0);
          
              /*
               * Thread to unpark is held in successor, which is normally
               * just the next node.  But if cancelled or apparently null,
               * traverse backwards from tail to find the actual
               * non-cancelled successor.
               */
              Node s = node.next;
              if (s == null || s.waitStatus > 0) {
                  s = null;
                  for (Node t = tail; t != null && t != node; t = t.prev)
                      if (t.waitStatus <= 0)
                          s = t;
              }
              if (s != null)
                  LockSupport.unpark(s.thread);
          }

          release方法中,總是總head節點開始向后查找sucessor。只有當該sucessor的waitStatus被設置的情況下才會調用unparkSuccessor。unparkSuccessor方法中首先清除之前設置的Node.waitStatus,然后向后查找并且喚醒第一個需要被喚醒的sucessor。需要注意的是,if (s == null || s.waitStatus > 0)這個分支中,查找是從tail節點開始,根據prev引用向前進行。在Inside AbstractQueuedSynchronizer (2) 中提到過,Node.next為null并不一定意味著沒有sucessor,雖然WaitQueue是個雙向鏈表,但是根據next引用向后查找sucessor不靠譜,而根據prev引用向前查找predecessor總是靠譜。

          3.5 Fairness

          到目前為止我們已經知道,WaitQueue是個FIFO的隊列,喚醒也總是從head開始。但是AbstractQueuedSynchronizer卻并不一定是公平的(實際上,大多數情況下都是在非公平模式下工作)。如果在看一遍acquire方法會發現,tryAcquire的調用順序先于acquireQueued,也就是說后來的線程可能在等待中的線程之前acquire成功。這種場景被稱為barging FIFO strategy,它能提供更高的吞吐量。

          大多數AbstractQueuedSynchronizer的子類都同時提供了公平和非公平的實現,例如ReentrantLock提供了NonfairSync和FairSync。例如其FairSync的tryAcquire方法如下:

          protected final boolean tryAcquire(int acquires) {
              final Thread current = Thread.currentThread();
              int c = getState();
              if (c == 0) {
                  if (!hasQueuedPredecessors() &&
                      compareAndSetState(0, acquires)) {
                      setExclusiveOwnerThread(current);
                      return true;
                  }
              }
              else if (current == getExclusiveOwnerThread()) {
                  int nextc = c + acquires;
                  if (nextc < 0)
                      throw new Error("Maximum lock count exceeded");
                  setState(nextc);
                  return true;
              }
              return false;
          }

          tryAcquire方法返回true的條件之一是!hasQueuedPredecessors() 。hasQueuedPredecessors的代碼如下:

          public final boolean hasQueuedPredecessors() {
              // The correctness of this depends on head being initialized
              // before tail and on head.next being accurate if the current
              // thread is first in queue.
              Node t = tail; // Read fields in reverse initialization order
              Node h = head;
              Node s;
              return h != t &&
                  ((s = h.next) == null || s.thread != Thread.currentThread());
          }

          綜上, FairSync優先確保等待中線程先acquire成功。但是公平性也不是絕對的:在一個多線程并發的環境下,就算鎖的獲取是公平的,也不保證后續的其它處理過程的先后順序。

          既然默認情況下使用的都是NonfairSync,那么FairSync適合什么樣的場景呢?如果被鎖所保護的代碼段的執行時間比較長,而應用又不能接受線程饑餓(NonfairSync可能會導致雖然某個線程長時間排隊,但是仍然無法獲得鎖的情況)的場景下可以考慮使用FairSync。對于ReentrantLock,在其構造函數中傳入true,即可構造一把公平鎖。

          posted on 2012-01-07 23:37 whitesock 閱讀(192) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 锡林浩特市| 太仓市| 景泰县| 炉霍县| 郧西县| 河津市| 双流县| 宜兰市| 舒城县| 石屏县| 郓城县| 隆尧县| 巢湖市| 枣阳市| 常德市| 石屏县| 腾冲县| 凭祥市| 公安县| 固镇县| 桐城市| 房山区| 林周县| 黔江区| 涟源市| 靖宇县| 陇川县| 垦利县| 古丈县| 壶关县| 永登县| 宝清县| 区。| 桃江县| 临漳县| 浦东新区| 白河县| 页游| 盘山县| 汝州市| 彭州市|