ITPub博客

首页 > 数据库 > 数据库开发技术 > JAVA并发之AQS基础总结

JAVA并发之AQS基础总结

原创 数据库开发技术 作者:服务端技术栈 时间:2019-05-03 22:38:41 0 删除 编辑

JDK1.5之后JAVA团队为开发者提供了一套面向并发编程的API,他们位于juc包(java.util.concurrent)中,其中包括了锁,同步容器类,支持原子操作类型的类,线程池等,而其中锁在开发中使用较多的工具之一,如ReentrantLock,CountDownLatch等,他们都是基于juc包中的java.util.concurrent.locks.AbstractQueuedSynchronizer类实现的。

本篇文章我们将通过源码的角度来分析AbstractQueuedSynchronizer的实现原理,分析下它本身有哪些能力,并给开发者提供了哪些扩展能力。

AbstractQueuedSynchronizer位于java.util.concurrent.locks包下面,

如下图所示:


AbstractQueuedSynchronizer:

它是一个提供实现阻塞锁和相关锁(同步器(信号量、事件等))的基础框架,它依赖一个先进先出(FIFO)等待队列。它通过一个原子性的整形值表示状态,子类必须实现更改此状态的受保护方法,哪个对象调用该方法改变状态则说明哪个对象在获取或释放资源。这个类默认支持独占锁和共享锁两种模式。

既然它是一个框架,那么我就来了解下它是怎么构建框架的

1、首先它拥有一个队列,队列元素是内部内Node类的实例,下面我们看下Node的定义:


// 等待线程队列节点元素类,

<pre>

*      +------+  prev +-----+       +-----+

* head |      | <---- |     | <---- |     |  tail

*      +------+       +-----+       +-----+

* </pre>

先进先出的队列,一个线程入队只需要操作尾部节点(需要原子性),出队只需要更新头节点(不需要原子性)

static final class Node {

   /** 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;

   /** 被取消状态,不再需要继续CAS获取锁 */

   static final int CANCELLED =  1;

   /** 后继线程需要停止休眠,继续运行起来 */

   static final int SIGNAL    = -1;

   /** waitStatus value to indicate thread is waiting on condition */

   static final int CONDITION = -2;

   /**

    * waitStatus value to indicate the next acquireShared should

    * unconditionally propagate

    */

   static final int PROPAGATE = -3;

   /**

    * Status field, taking on only the values:

    *   SIGNAL:    

//  如果当前节点的后继节点是阻塞的,当前节点释放同步资源或者取消等待的时候必须唤醒它的后继节点,为了避免竞争,请求同步资源方法必须首先表明他们需要一个信号,重试原子获取同步资源,快速阻塞。

   volatile int waitStatus;

   /**

     前驱结点

    */

   volatile Node prev;

   /**

    *  后继节点

    */

   volatile Node next;

   /**

    * 当前节点所属线程

    */

   volatile Thread thread;

   /**

    * Link to next node waiting on condition, or the special

    * value SHARED.  

    * 

    */

   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.  The null check could

    * be elided, but is present to help the VM.

    *

    * @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;

   }

}


// 线程节点入队列规则:

1、新节点前驱指向老尾节点

2、老尾节点后继指向新节点

3、尾节点设置为新的节点

/**

* Inserts node into queue, initializing if necessary. See picture above.

* @param node the node to insert

* @return node's predecessor

*/

private Node enq(final Node node) {

   for (;;) {

       Node t = tail;

              //队列为空的情况,需要初始化

       if (t == null) { // Must initialize

              // 原子性设置一个头节点,头节点没有线程与状态

           if (compareAndSetHead(new Node()))

               tail = head;

       } else {

           node.prev = t;

           if (compareAndSetTail(t, node)) {

               t.next = node;

               return t;

           }

       }

   }

}

//先尝试当前线程快速原子性入等待队列操作,失败则进行原子性自旋的入队列操作

/**

* Creates and enqueues node for current thread and given mode.

*

* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared

* @return the new node

*/

private Node addWaiter(Node mode) {

   Node node = new Node(Thread.currentThread(), mode);

// 先快速入等待队列,失败则进行自旋入队列,

   Node pred = tail;

   if (pred != null) {

       node.prev = pred;

       if (compareAndSetTail(pred, node)) {

           pred.next = node;

           return node;

       }

   }

   enq(node);

   return node;

}

// 设置头节点,并没有保障线程安全措施,达到出队列的操作,为了利于GC, 将头节点其他属性置空

/**

* Sets head of queue to be node, thus dequeuing. Called only by

* acquire methods.  Also nulls out unused fields for sake of GC

* and to suppress unnecessary signals and traversals.

*

* @param node the node

*/

private void setHead(Node node) {

   head = node;

   node.thread = null;

   node.prev = null;

}

//失败获取同步资源后,是否需要休眠,需要休眠的条件:

1、当前节点前驱节点是signal等待状态

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

   int ws = pred.waitStatus;

   if (ws == Node.SIGNAL)

       /*

        * This node has already set status asking a release

        * to signal it, so it can safely park.

        */

       return true;

   if (ws > 0) {

       /*

        * Predecessor was cancelled. Skip over predecessors and

        * indicate retry.

        */

       do {

// 不断删除取消等待状态的前驱节点,并将当前节点node与新的前驱节点绑定

           node.prev = pred = pred.prev;

       } while (pred.waitStatus > 0);

       pred.next = node;

   } else {

       /*

        * waitStatus must be 0 or PROPAGATE.  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, ws, Node.SIGNAL);

   }

   return false;

}

/**   唤醒后继节点

* Wakes up node's successor, if one exists.

*

* @param node the node

*/

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);

   /*

// 当前线程释放锁后,唤醒后继线程所在的节点,如果后继几点为null或者节点为取消等待状态,则从队里尾部往头部遍历,直到找到一个可以继续获取同步资源的节点

    * 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;

//如果当前节点后继节点为null,或者后继节点为取消等待状态,则从尾部向头部节点遍历,找到一个可以唤醒的合适节点

   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);

}

// 设置节点为取消获取同步资源状态

/**

* Cancels an ongoing attempt to acquire.

*

* @param node the node

*/

private void cancelAcquire(Node node) {

   // Ignore if node doesn't exist

   if (node == null)

       return;

   node.thread = null;

   // Skip cancelled predecessors

   Node pred = node.prev;

   while (pred.waitStatus > 0)

       node.prev = pred = pred.prev;

   // predNext is the apparent node to unsplice. CASes below will

   // fail if not, in which case, we lost race vs another cancel

   // or signal, so no further action is necessary.

   Node predNext = pred.next;

   // Can use unconditional write instead of CAS here.

   // After this atomic step, other Nodes can skip past us.

   // Before, we are free of interference from other threads.

   node.waitStatus = Node.CANCELLED;

//如果当前节点是尾部节点,设置当前节点前置节点为尾部节点,并设置新的尾节点为Null

   // If we are the tail, remove ourselves.

   if (node == tail && compareAndSetTail(node, pred)) {

       compareAndSetNext(pred, predNext, null);

   } else {

       // If successor needs signal, try to set pred's next-link

       // so it will get one. Otherwise wake it up to propagate.

       int ws;

// node不是尾部节点,也不是head节点后继节点时候,将node.next与node前置节点的next链接上

       if (pred != head &&

           ((ws = pred.waitStatus) == Node.SIGNAL ||

            (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&

           pred.thread != null) {

           Node next = node.next;

           if (next != null && next.waitStatus <= 0)

               compareAndSetNext(pred, predNext, next);

       } else {

//  node是头节点的后置节点的时候,将Node的后置节点唤醒,

           unparkSuccessor(node);

       }

//  node的后继节点与队列断开,辅助系统gc

       node.next = node; // help GC

   }

}

//检测当前Node节点是否需要休眠

//休眠的条件:node的前置pre节点等待状态为signal:表明已经准备唤醒后置节点node

/**

* Checks and updates status for a node that failed to acquire.

* Returns true if thread should block. This is the main signal

* control in all acquire loops.  Requires that pred == node.prev.

*

* @param pred node's predecessor holding status

* @param node the node

* @return {@code true} if thread should block

*/

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

   int ws = pred.waitStatus;

   if (ws == Node.SIGNAL)          //前置节点已经设置成signal,准备唤醒后置节点,//后置节点Node可以休眠

       /*

        * This node has already set status asking a release

        * to signal it, so it can safely park.

        */

       return true;

   if (ws > 0) {

       /*

        * Predecessor was cancelled. Skip over predecessors and

        * indicate retry.

        */    

//  前置节点是取消等待状态,node节点不能进行休眠,将前置节点与Node解绑,并且不断找到非取消状态的前置节点,并与node重新绑定

       do {

           node.prev = pred = pred.prev;

       } while (pred.waitStatus > 0);

       pred.next = node;

   } else {

       /*   //前置节点不是取消状态,signal状态,为0,或者propagete状态,设置前置节点为signal状态,

        * waitStatus must be 0 or PROPAGATE.  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, ws, Node.SIGNAL);

   }

   return false;

}

---------------------------------------------------

-------独占模式获取同步资源方法----------------

---------------------------------------------------

/**

* Acquires in exclusive mode, ignoring interrupts.  Implemented

* by invoking at least once {@link #tryAcquire},

* returning on success.  Otherwise the thread is queued, possibly

* repeatedly blocking and unblocking, invoking {@link

* #tryAcquire} until success.  This method can be used

* to implement method {@link Lock#lock}.

*

* @param arg the acquire argument.  This value is conveyed to

*        {@link #tryAcquire} but is otherwise uninterpreted and

*        can represent anything you like.

*/

//请求同步资源,但是忽略等待请求资源过程中的中断(即不抛中断异常)

public final void acquire(int arg) {

   if (!tryAcquire(arg)    //尝试获取同步资源

&& acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //尝试获取同步资源不成功则将当前线程入队列,返回是否入队列过程中线程发了中断

       selfInterrupt();   等待获取资源过程中线程发了中断,设置当前线程中断标志

}

//tryAcquire方法需要子类自行实现,如何获取锁,且获取是否成功

protected boolean tryAcquire(int arg) {

   throw new UnsupportedOperationException();

}

// addWaiter方法单纯的将当前线程加入到等待队列中

//acquireQueued函数 为已经处于等待队列中线程获取同步资源,获取过程中不中断,只返回是否中断

final boolean acquireQueued(final Node node, int arg) {

   boolean failed = true;

   try {

       boolean interrupted = false;

//利用死循环,不断为当前线程请求同步资源

       for (;;) {

           final Node p = node.predecessor();  //获取node的前置节点

//如果node节点的前置节点是头节点,则node节点属于头节点的后继节点,属于第一个有获取同步资源的资格的线程,立即尝试一次获取同步资源

           if (p == head && tryAcquire(arg)) {

同步资源获取成功,node设置成头节点

               setHead(node);

               p.next = null; // help GC

               failed = false;

               return interrupted;

           }

//获取失败的情况,检测下当前线程是否需要阻塞。需要阻塞就调用LockSupport.park(this); 将当前线程阻塞(等到调用unpark再继续执行获取资源代码),再判断是否有被中断过

           if (shouldParkAfterFailedAcquire(p, node) &&

               parkAndCheckInterrupt())

               interrupted = true;

       }

   } finally {

       if (failed)   //如果在获取同步资源过程中,失败了,且还没获取到资源,就将当前线程从等待队列中移除,进行出队操作。

           cancelAcquire(node);

   }

}

//interrupt();方法将中断 线程的sleep/wait方法,不会中断正在执行的代码 获取同步资源,响应中断

public final void acquireInterruptibly(int arg)

       throws InterruptedException {

   if (Thread.interrupted()) // 如果当前线程中断标志为true,则抛出中断异常,和方法名相呼应

       throw new InterruptedException();

   if (!tryAcquire(arg))    // 尝试获取同步资源,如果没获取成功,则不断自旋获取

       doAcquireInterruptibly(arg);

}

/tryAcquire(arg)    // 尝试获取同步资源,子类实现,典型的摸板方法模式响应中断的进行获取同步资源

/**

* Acquires in exclusive interruptible mode.

* @param arg the acquire argument

*/

private void doAcquireInterruptibly(int arg)

   throws InterruptedException {

   final Node node = addWaiter(Node.EXCLUSIVE);

   boolean failed = true;

   try {

       for (;;) {

           final Node p = node.predecessor();

           if (p == head && tryAcquire(arg)) {

               setHead(node);

               p.next = null; // help GC

               failed = false;

               return;

           }

           if (shouldParkAfterFailedAcquire(p, node) &&

               parkAndCheckInterrupt())

//和忽略中断的请i求同步资源方法的唯一区别,就是等待资源过程中发生过中断,那么抛出中断异常

               throw new InterruptedException();

       }

   } finally {

       if (failed)

           cancelAcquire(node);

   }

}

// 在固定时间内获取同步资源,如果超时则获取失败

* Acquires in exclusive timed mode.

*

* @param arg the acquire argument

* @param nanosTimeout max wait time

* @return {@code true} if acquired

*/

private boolean doAcquireNanos(int arg, long nanosTimeout)

       throws InterruptedException {

   if (nanosTimeout <= 0L)

       return false;

/   //获取资源最大时间

   final long deadline = System.nanoTime() + nanosTimeout;

   final Node node = addWaiter(Node.EXCLUSIVE);

   boolean failed = true;

   try {

       for (;;) {

           final Node p = node.predecessor();

           if (p == head && tryAcquire(arg)) {

               setHead(node);

               p.next = null; // help GC

               failed = false;

               return true;

           }

           nanosTimeout = deadline - System.nanoTime();

           if (nanosTimeout <= 0L) //现在的时间已经最大的超时时间,返回获取失败

               return false;

           if (shouldParkAfterFailedAcquire(p, node) &&

               nanosTimeout > spinForTimeoutThreshold)

// 如果需要休息了,则剩余等待时间(可休息的时间)是否大于配置值,大于则进行休息,小于就没必要休息了。

               LockSupport.parkNanos(this, nanosTimeout);

           if (Thread.interrupted())

               throw new InterruptedException();

       }

   } finally {

       if (failed)

           cancelAcquire(node);

   }

}

//释放在独占模式下获取到的同步资源

/**

* Releases in exclusive mode.  Implemented by unblocking one or

* more threads if {@link #tryRelease} returns true.

* This method can be used to implement method {@link Lock#unlock}.

*

* @param arg the release argument.  This value is conveyed to

*        {@link #tryRelease} but is otherwise uninterpreted and

*        can represent anything you like.

* @return the value returned from {@link #tryRelease}

*/

public final boolean release(int arg) {

   if (tryRelease(arg)) {  //通过子类实现释放函数,利用摸板模式

       Node h = head;  //当前线程已经成功获取到了同步资源,且同步资源释放成功了,唤醒头节点的后继节点

       if (h != null && h.waitStatus != 0)

           unparkSuccessor(h);

       return true;

   }

   return false;

}

----------------以上,就已经分析完了AQS给独占式同步资源操作的API方法

----------------下面开始,介绍AQS给共享式同步资源操作的API方法

//首先也是一个忽略中断的请求同步资源方法

/**

* Acquires in shared mode, ignoring interrupts.  Implemented by

* first invoking at least once {@link #tryAcquireShared},

* returning on success.  Otherwise the thread is queued, possibly

* repeatedly blocking and unblocking, invoking {@link

* #tryAcquireShared} until success.

*

* @param arg the acquire argument.  This value is conveyed to

*        {@link #tryAcquireShared} but is otherwise uninterpreted

*        and can represent anything you like.

*/

public final void acquireShared(int arg) {

   if (tryAcquireShared(arg) < 0)   // 尝试获取共享资源,子类实现,判断是否成功与独占式不同,小于0代表获取失败,

       doAcquireShared(arg);   //获取失败,需要将当前线程加入等待队列

}

//非响应式中断,请求共享资源,和独占式获取逻辑类似

/**

* Acquires in shared uninterruptible mode.

* @param arg the acquire argument

*/

private void doAcquireShared(int arg) {

   final Node node = addWaiter(Node.SHARED);

   boolean failed = true;

   try {

       boolean interrupted = false;

       for (;;) {

           final Node p = node.predecessor();

           if (p == head) {

               int r = tryAcquireShared(arg);

               if (r >= 0) {

                   setHeadAndPropagate(node, r);

                   p.next = null; // help GC

                   if (interrupted)

                       selfInterrupt();

                   failed = false;

                   return;

               }

           }

           if (shouldParkAfterFailedAcquire(p, node) &&

               parkAndCheckInterrupt())

               interrupted = true;

       }

   } finally {

       if (failed)

           cancelAcquire(node);

   }

}

//会对中断进行响应的,请求共享资源

public final void acquireSharedInterruptibly(int arg)

       throws InterruptedException {

   if (Thread.interrupted())

       throw new InterruptedException();

   if (tryAcquireShared(arg) < 0)

       doAcquireSharedInterruptibly(arg);

}

//响应中断的请求共享资源

/**

* Acquires in shared interruptible mode.

* @param arg the acquire argument

*/

private void doAcquireSharedInterruptibly(int arg)

   throws InterruptedException {

   final Node node = addWaiter(Node.SHARED);

   boolean failed = true;

   try {

       for (;;) {

           final Node p = node.predecessor();

           if (p == head) {

               int r = tryAcquireShared(arg);

               if (r >= 0) {

                   setHeadAndPropagate(node, r);

                   p.next = null; // help GC

                   failed = false;

                   return;

               }

           }

           if (shouldParkAfterFailedAcquire(p, node) &&

               parkAndCheckInterrupt())

// 当前线程在等待获取资源的过程中,中断过,则抛出中断异常来响应中断

               throw new InterruptedException();

       }

   } finally {

       if (failed)

           cancelAcquire(node);

   }

}

//在响应中断的基础上,加上等待的超时时间,获取同步资源

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)

       throws InterruptedException {

   if (Thread.interrupted())

       throw new InterruptedException();

   return tryAcquireShared(arg) >= 0 ||

//在允许的超时时间内,获取同步资源

       doAcquireSharedNanos(arg, nanosTimeout);

}

//根据最大超时时间进行同步资源获取

/**

* Acquires in shared timed mode.

*

* @param arg the acquire argument

* @param nanosTimeout max wait time

* @return {@code true} if acquired

*/

private boolean doAcquireSharedNanos(int arg, long nanosTimeout)

       throws InterruptedException {

   if (nanosTimeout <= 0L)

       return false;

   final long deadline = System.nanoTime() + nanosTimeout;

   final Node node = addWaiter(Node.SHARED);

   boolean failed = true;

   try {

       for (;;) {

           final Node p = node.predecessor();

           if (p == head) {

               int r = tryAcquireShared(arg);

               if (r >= 0) {

                   setHeadAndPropagate(node, r);

                   p.next = null; // help GC

                   failed = false;

                   return true;

               }

           }

           nanosTimeout = deadline - System.nanoTime();

           if (nanosTimeout <= 0L)

               return false;

           if (shouldParkAfterFailedAcquire(p, node) &&

               nanosTimeout > spinForTimeoutThreshold)

               LockSupport.parkNanos(this, nanosTimeout);

           if (Thread.interrupted())

               throw new InterruptedException();

       }

   } finally {

       if (failed)

           cancelAcquire(node);

   }

}、

//释放共享模式下的同步资源的

public final boolean releaseShared(int arg) {

   if (tryReleaseShared(arg)) {  //尝试释放同步资源,子类实现,模板模式体现

       doReleaseShared();  // 执行资源释放操作

       return true;

   }

   return false;

}

//执行共享模式下同步资源释放操作

/**

* Release action for shared mode -- signals successor and ensures

* propagation. (Note: For exclusive mode, release just amounts

* to calling unparkSuccessor of head if it needs signal.)

*/

private void doReleaseShared() {

   /*

    * Ensure that a release propagates, even if there are other

    * in-progress acquires/releases.  This proceeds in the usual

    * way of trying to unparkSuccessor of head if it needs

    * signal. But if it does not, status is set to PROPAGATE to

    * ensure that upon release, propagation continues.

    * Additionally, we must loop in case a new node is added

    * while we are doing this. Also, unlike other uses of

    * unparkSuccessor, we need to know if CAS to reset status

    * fails, if so rechecking.

    */

   for (;;) {

       Node h = head;

       if (h != null && h != tail) {

           int ws = h.waitStatus;

           if (ws == Node.SIGNAL) {

               if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))

                   continue;            // loop to recheck cases

               unparkSuccessor(h); //唤醒后继

           }

           else if (ws == 0 &&

                    !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))

               continue;                // loop on failed CAS

       }

       if (h == head)                   // loop if head changed

           break;

   }

}


原文作者:服务端技术栈;如有侵权,请联系管理员删除。

下一篇: SpringAOP源码解析
请登录后发表评论 登录
全部评论

注册时间:2019-03-08

  • 博文量
    97
  • 访问量
    41428