whitesock

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            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()
          

          這幾個方法的默認(rèn)實現(xiàn)是拋出UnsupportedOperationException,子類可以根據(jù)需要進(jìn)行改寫。

          AbstractQueuedSynchronizer中最基本的acquire流程的相關(guān)代碼如下:

          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失敗,那么當(dāng)前線程可能會被enqueue到WaitQueue,然后被阻塞。 shouldParkAfterFailedAcquire方法會確保每個線程在被阻塞之前,其對應(yīng)WaitQueue中的節(jié)點的waitStatus被設(shè)置為Node.SIGNAL(-1),以便在release時避免不必要的unpark操作。此外shouldParkAfterFailedAcquire還會清理WaitQueue中已經(jīng)超時或者取消的Node。需要注意的是,在某個線程最終被阻塞之前,tryAcquire可能會被多次調(diào)用。

          AbstractQueuedSynchronizer中最基本的release流程的相關(guān)代碼如下:

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

          3.5 Fairness

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

          大多數(shù)AbstractQueuedSynchronizer的子類都同時提供了公平和非公平的實現(xiàn),例如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優(yōu)先確保等待中線程先acquire成功。但是公平性也不是絕對的:在一個多線程并發(fā)的環(huán)境下,就算鎖的獲取是公平的,也不保證后續(xù)的其它處理過程的先后順序。

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

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

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 石首市| 芜湖市| 砚山县| 都昌县| 松阳县| 措美县| 明溪县| 淮滨县| 蕉岭县| 台北市| 萍乡市| 嵊泗县| 逊克县| 土默特左旗| 清水县| 黄陵县| 思南县| 宁陵县| 苏尼特左旗| 富顺县| 云浮市| 浙江省| 台东县| 宁乡县| 大庆市| 墨脱县| 紫金县| 合肥市| 贵州省| 梅河口市| 盈江县| 西青区| 阜阳市| 团风县| 温泉县| 大余县| 卫辉市| 新泰市| 沂源县| 大石桥市| 仙居县|