yuyee

          AbstractQueuedSynchronizer看看


          API中的解釋:為實現依賴于先進先出 (FIFO) 等待隊列的阻塞鎖定和相關同步器(信號量、事件,等等)提供一個框架。此類的設計目標是成為依靠單個原子 int 值來表示狀態的大多數同步器的一個有用基礎。子類必須定義更改此狀態的受保護方法,并定義哪種狀態對于此對象意味著被獲取或被釋放。假定這些條件之后,此類中的其他方法就可以實現所有排隊和阻塞機制。子類可以維護其他狀態字段,但只是為了獲得同步而只追蹤使用 getState()getState()getState()setState(int)compareAndSetState(int, int) 方法來操作以原子方式更新的 int 值。
          此類采用模板模式設計,此類為一個抽象類,但是沒抽象方法,每個sync子類需要實現5個受保護的方法

          這個5個方法在AbstractQueuedSynchronizer 都拋出throw new UnsupportedOperationException();
          AbstractQueuedSynchronizer 中有3個屬性:主要聲明一個狀態和一個wait queue,通過

          wait queue中Node 為一個雙向鏈表,需要去理解Node中幾個靜態字段值的意義,下面為他的源碼:
          static final class Node {
                  /** waitStatus value to indicate thread has cancelled */
                  static final int CANCELLED =  1;
                  /** waitStatus value to indicate successor's thread needs unparking */
                  static final int SIGNAL    = -1;
                  /** waitStatus value to indicate thread is waiting on condition */
                  static final int CONDITION = -2;
                  /** Marker to indicate a node is waiting in shared mode */
                  static final Node SHARED = new Node();
                  /** Marker to indicate a node is waiting in exclusive mode */
                  static final Node EXCLUSIVE = null;

                  /**
                   * Status field, taking on only the values:
                   *   SIGNAL:     The successor of this node is (or will soon be)
                   *               blocked (via park), so the current node must
                   *               unpark its successor when it releases or
                   *               cancels. To avoid races, acquire methods must
                   *               first indicate they need a signal,
                   *               then retry the atomic acquire, and then,
                   *               on failure, block.
                   *   CANCELLED:  This node is cancelled due to timeout or interrupt.
                   *               Nodes never leave this state. In particular,
                   *               a thread with cancelled node never again blocks.
                   *   CONDITION:  This node is currently on a condition queue.
                   *               It will not be used as a sync queue node until
                   *               transferred. (Use of this value here
                   *               has nothing to do with the other uses
                   *               of the field, but simplifies mechanics.)
                   *   0:          None of the above
                   *
                   * The values are arranged numerically to simplify use.
                   * Non-negative values mean that a node doesn't need to
                   * signal. So, most code doesn't need to check for particular
                   * values, just for sign.
                   *
                   * The field is initialized to 0 for normal sync nodes, and
                   * CONDITION for condition nodes.  It is modified only using
                   * CAS.
                   */
                  volatile int waitStatus;

                  /**
                   * Link to predecessor node that current node/thread relies on
                   * for checking waitStatus. Assigned during enqueing, and nulled
                   * out (for sake of GC) only upon dequeuing.  Also, upon
                   * cancellation of a predecessor, we short-circuit while
                   * finding a non-cancelled one, which will always exist
                   * because the head node is never cancelled: A node becomes
                   * head only as a result of successful acquire. A
                   * cancelled thread never succeeds in acquiring, and a thread only
                   * cancels itself, not any other node.
                   */
                  volatile Node prev;

                  /**
                   * Link to the successor node that the current node/thread
                   * unparks upon release. Assigned once during enqueuing, and
                   * nulled out (for sake of GC) when no longer needed.  Upon
                   * cancellation, we cannot adjust this field, but can notice
                   * status and bypass the node if cancelled.  The enq operation
                   * does not assign next field of a predecessor until after
                   * attachment, so seeing a null next field does not
                   * necessarily mean that node is at end of queue. However, if
                   * a next field appears to be null, we can scan prev's from
                   * the tail to double-check.
                   */
                  volatile Node next;

                  /**
                   * The thread that enqueued this node.  Initialized on
                   * construction and nulled out after use.
                   */
                  volatile Thread thread;

                  /**
                   * Link to next node waiting on condition, or the special
                   * value SHARED.  Because condition queues are accessed only
                   * when holding in exclusive mode, we just need a simple
                   * linked queue to hold nodes while they are waiting on
                   * conditions. They are then transferred to the queue to
                   * re-acquire. And because conditions can only be exclusive,
                   * we save a field by using special value to indicate shared
                   * mode.
                   */
                  Node nextWaiter;

                  /**
                   * Returns true if node is waiting in shared mode
                   */
                  final boolean isShared() {
                      return nextWaiter == SHARED;
                  }

                  /**
                   * Returns previous node, or throws NullPointerException if
                   * null.  Use when predecessor cannot be null.
                   * @return the predecessor of this node
                   */
                  final Node predecessor() throws NullPointerException {
                      Node p = prev;
                      if (p == null)
                          throw new NullPointerException();
                      else
                          return p;
                  }

                  Node() {    // Used to establish initial head or SHARED marker
                  }

                  Node(Thread thread, Node mode) {     // Used by addWaiter
                      this.nextWaiter = mode;
                      this.thread = thread;
                  }

                  Node(Thread thread, int waitStatus) { // Used by Condition
                      this.waitStatus = waitStatus;
                      this.thread = thread;
                  }


              }
          獲取鎖定調用的方法,其實這個方法是阻塞的:
            public final void acquire(int arg) {
                  if (!tryAcquire(arg) &&
                      acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                      selfInterrupt();
              }
          如果獲取不成功則調用如下方法:
             final boolean acquireQueued(final Node node, int arg) {
                  try {
                      boolean interrupted = false;
                      for (;;) {
                          final Node p = node.predecessor();
                          if (p == head && tryAcquire(arg)) {//當節點是頭節點且獨占時才返回
                              setHead(node);
                              p.next = null; // help GC
                              return interrupted;
                          }
                          if (shouldParkAfterFailedAcquire(p, node) &&
                              parkAndCheckInterrupt())//阻塞并判斷是否打斷,其實這個判斷才是自旋鎖真正的猥瑣點,
          意思是如果你的前繼節點不是head,而且當你的前繼節點狀態是Node.SIGNAL時,你這個線程將被park(),直到另外的線程release時,發現head.next是你這個node時,才unpark,你才能繼續循環并獲取鎖
                              interrupted = true;
                      }
          shouldParkAfterFailedAcquire這個方法刪除所有waitStatus>0也就是CANCELLED狀態的Node,并設置前繼節點為signal
           private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
                  int s = pred.waitStatus;
                  if (s < 0)
                      /*
                       * This node has already set status asking a release
                       * to signal it, so it can safely park
                       */
                      return true;
                  if (s > 0) {
                      /*
                       * Predecessor was cancelled. Skip over predecessors and
                       * indicate retry.
                       */
             do {
          node.prev = pred = pred.prev;
             } while (pred.waitStatus > 0);
             pred.next = node;
          }
                  else
                      /*
                       * Indicate that we need a signal, but don't park yet. Caller
                       * will need to retry to make sure it cannot acquire before
                       * parking.
                       */
                      compareAndSetWaitStatus(pred, 0, Node.SIGNAL);
                  return false;
              }


          使用LockSupport.park(this),禁用當前線程
          private final boolean parkAndCheckInterrupt() {
                  LockSupport.park(this);//block
                  return Thread.interrupted();
              }
          釋放鎖:
              public final boolean release(int arg) {
                  if (tryRelease(arg)) {
                      Node h = head;
                      if (h != null && h.waitStatus != 0)
                          unparkSuccessor(h);//unblock
                      return true;
                  }
                  return false;
              }
          private void unparkSuccessor(Node node) {
                  /*
                   * Try to clear status in anticipation of signalling.  It is
                   * OK if this fails or if status is changed by waiting thread.
                   */
                  compareAndSetWaitStatus(node, Node.SIGNAL, 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);
              }


                  } catch (RuntimeException ex) {
                      cancelAcquire(node);
                      throw ex;
                  }
              }
            

          看下ReentrantLock鎖中sync的實現:
           static abstract class Sync extends AbstractQueuedSynchronizer {
                  private static final long serialVersionUID = -5179523762034025860L;

                  /**
                   * Performs {@link Lock#lock}. The main reason for subclassing
                   * is to allow fast path for nonfair version.
                   */
                  abstract void lock();

                  /**
                   * Performs non-fair tryLock.  tryAcquire is
                   * implemented in subclasses, but both need nonfair
                   * try for trylock method.
                   */
                  final boolean nonfairTryAcquire(int acquires) {
                      final Thread current = Thread.currentThread();
                      int c = getState();
                      if (c == 0) {
                          if (compareAndSetState(0, acquires)) {
                              setExclusiveOwnerThread(current);
                              return true;
                          }
                      }
                      else if (current == getExclusiveOwnerThread()) {
                          int nextc = c + acquires;
                          if (nextc < 0) // overflow
                              throw new Error("Maximum lock count exceeded");
                          setState(nextc);
                          return true;
                      }
                      return false;
                  }

                  protected final boolean tryRelease(int releases) {
                      int c = getState() - releases;
                      if (Thread.currentThread() != getExclusiveOwnerThread())
                          throw new IllegalMonitorStateException();
                      boolean free = false;
                      if (c == 0) {
                          free = true;
                          setExclusiveOwnerThread(null);
                      }
                      setState(c);
                      return free;
                  }

                  protected final boolean isHeldExclusively() {
                      // While we must in general read state before owner,
                      // we don't need to do so to check if current thread is owner
                      return getExclusiveOwnerThread() == Thread.currentThread();
                  }

                  final ConditionObject newCondition() {
                      return new ConditionObject();
                  }

                  // Methods relayed from outer class

                  final Thread getOwner() {
                      return getState() == 0 ? null : getExclusiveOwnerThread();
                  }

                  final int getHoldCount() {
                      return isHeldExclusively() ? getState() : 0;
                  }

                  final boolean isLocked() {
                      return getState() != 0;
                  }

                  /**
                   * Reconstitutes this lock instance from a stream.
                   * @param s the stream
                   */
                  private void readObject(java.io.ObjectInputStream s)
                      throws java.io.IOException, ClassNotFoundException {
                      s.defaultReadObject();
                      setState(0); // reset to unlocked state
                  }
              }
          非公平規則下nonfairTryAcquire,獲取當前鎖的state,通過CAS原子操作設置為1,并將當前線程設置為獨占線程,如果當前線程已經拿了鎖,則state增加1
          公平鎖中 有如下判斷:
          if (isFirst(current) &&//判斷頭元素
                              compareAndSetState(0, acquires)) {
                              setExclusiveOwnerThread(current);
                              return true;
                          }
          在獲取鎖步驟:
          1.調用tryAcquire來獲取,如果失敗,則進入2
          2.調用addWaiter,以獨占模式將node放到tail位置
          3.調用acquireQueued方法,此方法阻塞,直到node的pre為head,并成功獲取鎖定,也可能存在阻塞并打斷情況
          釋放鎖的步驟:
          1.放棄排他鎖持有權
          2.unpark 節點的下一個blocked節點

          公平鎖與非公平鎖:從代碼上看,非公平鎖是讓當前線程優先獨占,而公平鎖則是讓等待時間最長的線程優先,非公平的可能讓其他線程沒機會執行,而公平的則可以讓等待時間最長的先執行,但是性能上會差點

          posted on 2010-11-09 15:30 羔羊 閱讀(821) 評論(0)  編輯  收藏 所屬分類: concurrent

          主站蜘蛛池模板: 古交市| 金乡县| 通州区| 华安县| 轮台县| 浦北县| 长宁区| 江口县| 通州区| 庆安县| 双峰县| 江陵县| 仪陇县| 洛川县| 渭源县| 新乐市| 西乡县| 清水县| 石阡县| 溆浦县| 红原县| 政和县| 芷江| 奈曼旗| 黑水县| 古田县| 黔南| 广昌县| 略阳县| 绥芬河市| 灵台县| 濮阳市| 日喀则市| 嫩江县| 韶山市| 宁明县| 花莲市| 新竹县| 丹阳市| 克什克腾旗| 南丹县|