聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一)

AQS是AbstractQueuedSynchronizer的缩写,AQS是Java并包里大部分同步器的基础构件,利用AQS可以很方便的创建锁和同步器。它封装了一个状态,提供了一系列的获取和释放操作,这些获取和释放操作都是基于状态的。它的基本思想是由AQS负责管理同步器类中的状态,其他的同步器比如可重入锁ReentrantLock, 信号量Semaphore基于各自的特点来调用AQS提供了基础能力进行状态的同步。

在AQS的Javadoc里面提到它是CLHLock的变种,在聊聊高并发(八)实现几种自旋锁(三) 这篇文章中我们说了如何利用CLH锁来构件自旋锁,回顾一下CLHLock的一些基本特点:

1. CLHLock是一种队列自旋锁的实现,提供了FIFO先来先服务的公平性

2. 利用一个原子变量AtomicReference tail的CAS操作来构件一个虚拟的链式结构

3. 节点Node维护一个volatile状态,维护一个prev指针指向前一个节点,获取锁时每个线程在prev节点的状态上自旋

4. 当线程释放锁时,只需要修改自身状态即可,后续节点会观察到volatile状态的改动而获取锁

AQS既然是CLHLock的一种变种,那么

1. 也维护以了一个基本的队列结构

2. 也是提供了一个Tail指针从队尾通过CAS操作入队列。

3. 提供了一个volatile类型的int值来维护状态

 
  1. public abstract class AbstractQueuedSynchronizer

  2. extends AbstractOwnableSynchronizer

  3. implements java.io.Serializable {

  4.  private transient volatile Node head;

  5.  
  6.     private transient volatile Node tail;

  7.  
  8.     private volatile int state;

  9.  
  10.  
  11.     protected final int getState() {

  12.         return state;

  13.     }

  14.  
  15.   

  16.     protected final void setState(int newState) {

  17.         state = newState;

  18.     }

  19.  
  20.  
  21.     protected final boolean compareAndSetState(int expect, int update) {

  22.         // See below for intrinsics setup to support this

  23.         return unsafe.compareAndSwapInt(this, stateOffset, expect, update);

  24.     }

  25.  
  26. ..................

  27. }

与标准CLHLock实现不同的是,AQS不是一个自旋锁,它提供了更加丰富的语意:

1. 提供了独享(exclusive)方式和共享(share)方式来获取/释放,比如锁是独占方式的,信号量semaphore是共享方式的,可以有多个线程进入临界区 

2. 支持可中断和不可中断的获取/释放

3. 支持普通的和具有时间限制的获取/释放

4. 提供了自旋和阻塞的切换,可以先自旋,如果等待时间长,可以阻塞

 
  1. /**

  2. * The number of nanoseconds for which it is faster to spin

  3. * rather than to use timed park. A rough estimate suffices

  4. * to improve responsiveness with very short timeouts.

  5. */

  6. static final long spinForTimeoutThreshold = 1000L;


AQS定义了两个内部类来辅助它的实现,一个是Node定义了队列中的节点,另一个是ConditionObject,是Condition接口的实现类,负责管理条件队列。关于条件队列更多内容可以看这篇 聊聊高并发(十四)理解Java中的管程,条件队列,Condition以及实现一个阻塞队列

先看下Node类,它比CLHLock中的Node有更多属性,除了完成基本的队列功能,还维护了是独享还是共享的模式信息

1. 维护了一个Node SHARED引用表示共享模式

2. 维护了一个Node EXCLUSIVE引用表示独占模式

3. 维护了几种节点等待的状态 waitStatus, 其中CANCELLED = 1是正数,表示取消状态,SIGNAL = -1,CONDITION = -2, PROPAGATE = -3都是负数,表示节点在条件队列的某个状态,SIGNAL表示后续节点需要被唤醒

4. 维护了Node prev引用,指向队列中的前一个节点,通过Tail的CAS操作来创建

5. 维护了Node next引用,指向队列中的下一个节点,也是在通过Tail入队列的时候设置的,这样就维护了一个双向队列

6. 维护了一个volatile的Thread引用,把一个节点关联到一个线程

7. 维护了Node nextWaiter引用,指向在条件队列中的下一个正在等待的节点,是给条件队列使用的。值得注意的是条件队列只有在独享状态下才使用

 
  1. static final class Node {

  2. /** Marker to indicate a node is waiting in shared mode */

  3. static final Node SHARED = new Node();

  4. /** Marker to indicate a node is waiting in exclusive mode */

  5. static final Node EXCLUSIVE = null;

  6.  
  7. /** waitStatus value to indicate thread has cancelled */

  8. static final int CANCELLED = 1;

  9. /** waitStatus value to indicate successor's thread needs unparking */

  10. static final int SIGNAL = -1;

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

  12. static final int CONDITION = -2;

  13. /**

  14. * waitStatus value to indicate the next acquireShared should

  15. * unconditionally propagate

  16. */

  17. static final int PROPAGATE = -3;

  18.  
  19. volatile int waitStatus;

  20.  
  21. volatile Node prev;

  22.  
  23. volatile Node next;

  24.  
  25. volatile Thread thread;

  26.  
  27. Node nextWaiter;

  28.  
  29. final boolean isShared() {

  30. return nextWaiter == SHARED;

  31. }

  32.  
  33. final Node predecessor() throws NullPointerException {

  34. Node p = prev;

  35. if (p == null)

  36. throw new NullPointerException();

  37. else

  38. return p;

  39. }

  40.  
  41. Node() { // Used to establish initial head or SHARED marker

  42. }

  43.  
  44. Node(Thread thread, Node mode) { // Used by addWaiter

  45. this.nextWaiter = mode;

  46. this.thread = thread;

  47. }

  48.  
  49. Node(Thread thread, int waitStatus) { // Used by Condition

  50. this.waitStatus = waitStatus;

  51. this.thread = thread;

  52. }

  53. }

再看一下ConditionObject,它是条件Condition接口的具体实现,维护了一个条件队列,条件队列是通过Node来构件的一个单向链表结构。底层的条件操作(等待和唤醒)使用LockSupport类来实现,在这篇中我们说了LockSupport底层使用sun.misc.Unsafe来提供条件队列的park和unpark操作。聊聊高并发(十七)解析java.util.concurrent各个组件(一) 了解sun.misc.Unsafe类

1. 维护了一个Node firstWaiter引用指向条件队列的队首节点

2. 维护了一个Node lastWaiter引用指向条件队列的队尾节点

3. 条件队列支持节点的取消退出机制,CANCELLED节点来表示这种取消状态

4. 支持限时等待机制

5. 支持可中断和不可中断的等待

我们来看几个典型的条件队列的操作实现

往条件队列里面加入一个等待节点,这个是await()方法的基本操作

1. 判断尾节点的状态是不是等待某个条件的状态(CONDITION),如果不是,就把CANCELLED节点从队列中踢出,然后把自己标记为尾节点

 
  1. public class ConditionObject implements Condition, java.io.Serializable {

  2. /** First node of condition queue. */

  3. private transient Node firstWaiter;

  4. /** Last node of condition queue. */

  5. private transient Node lastWaiter;

  6.  
  7. /**

  8.          * Adds a new waiter to wait queue.

  9.          * @return its new wait node

  10.          */

  11.         private Node addConditionWaiter() {

  12.             Node t = lastWaiter;

  13.             // If lastWaiter is cancelled, clean out.

  14.             if (t != null && t.waitStatus != Node.CONDITION) {

  15.                 unlinkCancelledWaiters();

  16.                 t = lastWaiter;

  17.             }

  18.             Node node = new Node(Thread.currentThread(), Node.CONDITION);

  19.             if (t == null)

  20.                 firstWaiter = node;

  21.             else

  22.                 t.nextWaiter = node;

  23.             lastWaiter = node;

  24.             return node;

  25.         }

  26.  
  27. private void unlinkCancelledWaiters() {

  28.             Node t = firstWaiter;

  29.             Node trail = null;

  30.             while (t != null) {

  31.                 Node next = t.nextWaiter;

  32.                 if (t.waitStatus != Node.CONDITION) {

  33.                     t.nextWaiter = null;

  34.                     if (trail == null)

  35.                         firstWaiter = next;

  36.                     else

  37.                         trail.nextWaiter = next;

  38.                     if (next == null)

  39.                         lastWaiter = trail;

  40.                 }

  41.                 else

  42.                     trail = t;

  43.                 t = next;

  44.             }

  45.         }

  46. .................

  47. }

从条件队列中唤醒一个节点,实际上doSignal只是把一个节点从条件队列中移除,然后加入到同步队列,并设置它在同步队列的前置节点的waitStatus = SIGNAL, 如果设置失败或者取消在条件队列等待,直接把这个节点的线程unpark唤醒,需要注意的是unpark操作只是把线程从等待状态转化为可运行状态,并不直接获得锁。

 
  1.  public final void signal() {

  2.             if (!isHeldExclusively())

  3.                 throw new IllegalMonitorStateException();

  4.             Node first = firstWaiter;

  5.             if (first != null)

  6.                 doSignal(first);

  7.         }

  8.  
  9.  
  10. /**

  11. * Removes and transfers nodes until hit non-cancelled one or

  12. * null. Split out from signal in part to encourage compilers

  13. * to inline the case of no waiters.

  14. * @param first (non-null) the first node on condition queue

  15. */

  16. private void doSignal(Node first) {

  17. do {

  18. if ( (firstWaiter = first.nextWaiter) == null)

  19. lastWaiter = null;

  20. first.nextWaiter = null;

  21. } while (!transferForSignal(first) &&

  22. (first = firstWaiter) != null);

  23. }

  24.  
  25.    final boolean transferForSignal(Node node) {

  26.         /*

  27.          * If cannot change waitStatus, the node has been cancelled.

  28.          */

  29.         if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))

  30.             return false;

  31.  
  32.         /*

  33.          * Splice onto queue and try to set waitStatus of predecessor to

  34.          * indicate that thread is (probably) waiting. If cancelled or

  35.          * attempt to set waitStatus fails, wake up to resync (in which

  36.          * case the waitStatus can be transiently and harmlessly wrong).

  37.          */

  38.         Node p = enq(node);

  39.         int ws = p.waitStatus;

  40.         if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))

  41.             LockSupport.unpark(node.thread);

  42.         return true;

  43.     }


Java线程的几种状态如下

支持中断的等待操作, 主要做了两个事情:新建一个Node进入条件队列等待被唤醒;从同步队列中移除并释放锁。它会相应线程的中断抛出中断异常,并且记录中断状态

 
  1. public final void await() throws InterruptedException {

  2. if (Thread.interrupted())

  3. throw new InterruptedException();

  4. Node node = addConditionWaiter();

  5. int savedState = fullyRelease(node);

  6. int interruptMode = 0;

  7. while (!isOnSyncQueue(node)) {

  8. LockSupport.park(this);

  9. if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

  10. break;

  11. }

  12. if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

  13. interruptMode = REINTERRUPT;

  14. if (node.nextWaiter != null) // clean up if cancelled

  15. unlinkCancelledWaiters();

  16. if (interruptMode != 0)

  17. reportInterruptAfterWait(interruptMode);

  18. }


不可中断的等待,也是先进入条件队列等待,并从同步队列出队列,释放锁。但是它不相应线程中断状态

 
  1. public final void awaitUninterruptibly() {

  2. Node node = addConditionWaiter();

  3. int savedState = fullyRelease(node);

  4. boolean interrupted = false;

  5. while (!isOnSyncQueue(node)) {

  6. LockSupport.park(this);

  7. if (Thread.interrupted())

  8. interrupted = true;

  9. }

  10. if (acquireQueued(node, savedState) || interrupted)

  11. selfInterrupt();

  12. }


限时等待,也是先进入条件队列等待,然后释放锁。轮询等待时间,当超时后再次进入同步队列,等待获得锁。如果获得了锁,就返回false. 如果在等待时被唤醒,就进入同步队列,等待获得锁,如果获得锁就返回true

 
  1. public final boolean await(long time, TimeUnit unit)

  2. throws InterruptedException {

  3. if (unit == null)

  4. throw new NullPointerException();

  5. long nanosTimeout = unit.toNanos(time);

  6. if (Thread.interrupted())

  7. throw new InterruptedException();

  8. Node node = addConditionWaiter();

  9. int savedState = fullyRelease(node);

  10. long lastTime = System.nanoTime();

  11. boolean timedout = false;

  12. int interruptMode = 0;

  13. while (!isOnSyncQueue(node)) {

  14. if (nanosTimeout <= 0L) {

  15. timedout = transferAfterCancelledWait(node);

  16. break;

  17. }

  18. if (nanosTimeout >= spinForTimeoutThreshold)

  19. LockSupport.parkNanos(this, nanosTimeout);

  20. if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

  21. break;

  22. long now = System.nanoTime();

  23. nanosTimeout -= now - lastTime;

  24. lastTime = now;

  25. }

  26. if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

  27. interruptMode = REINTERRUPT;

  28. if (node.nextWaiter != null)

  29. unlinkCancelledWaiters();

  30. if (interruptMode != 0)

  31. reportInterruptAfterWait(interruptMode);

  32. return !timedout;

  33. }


AQS使用了Unsafe直接操作内存来对字段进行CAS操作和设置值。

 
  1. private static final Unsafe unsafe = Unsafe.getUnsafe();

  2. private static final long stateOffset;

  3. private static final long headOffset;

  4. private static final long tailOffset;

  5. private static final long waitStatusOffset;

  6. private static final long nextOffset;

  7.  
  8. static {

  9. try {

  10. stateOffset = unsafe.objectFieldOffset

  11. (AbstractQueuedSynchronizer.class.getDeclaredField("state"));

  12. headOffset = unsafe.objectFieldOffset

  13. (AbstractQueuedSynchronizer.class.getDeclaredField("head"));

  14. tailOffset = unsafe.objectFieldOffset

  15. (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));

  16. waitStatusOffset = unsafe.objectFieldOffset

  17. (Node.class.getDeclaredField("waitStatus"));

  18. nextOffset = unsafe.objectFieldOffset

  19. (Node.class.getDeclaredField("next"));

  20.  
  21. } catch (Exception ex) { throw new Error(ex); }

  22. }

猜你喜欢

转载自blog.csdn.net/hellozhxy/article/details/82771718
今日推荐