Java多线程系列--深入理解AQS(一)

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

在AQS的Javadoc里面提到它是CLHLock的变种,在Java多线程系列--“JUC自旋锁三”(队列锁基于NUMA系统模式的MCSLock) 这篇文章中我们说了如何利用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值来维护状态

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
 private transient volatile Node head;
 
    private transient volatile Node tail;
 
    private volatile int state;
 
 
    protected final int getState() {
        return state;
    }
 
   
    protected final void setState(int newState) {
        state = newState;
    }
 
 
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
 
..................
}

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

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

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

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

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

 /**
     * The number of nanoseconds for which it is faster to spin
     * rather than to use timed park. A rough estimate suffices
     * to improve responsiveness with very short timeouts.
     */
    static final long spinForTimeoutThreshold = 1000L;

AQS定义了两个内部类来辅助它的实现,一个是Node定义了队列中的节点,另一个是ConditionObject,是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引用,指向在条件队列中的下一个正在等待的节点,是给条件队列使用的。值得注意的是条件队列只有在独享状态下才使用

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;
 
        /** 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;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;
 
        volatile int waitStatus;
 
        volatile Node prev;
 
        volatile Node next;
 
        volatile Thread thread;
 
        Node nextWaiter;
 
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
 
        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;
        }
    }

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

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

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

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

4. 支持限时等待机制

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

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

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

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

public class ConditionObject implements Condition, java.io.Serializable {
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;
 
        /**
         * Adds a new waiter to wait queue.
         * @return its new wait node
         */
        private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }
 
private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                Node next = t.nextWaiter;
                if (t.waitStatus != Node.CONDITION) {
                    t.nextWaiter = null;
                    if (trail == null)
                        firstWaiter = next;
                    else
                        trail.nextWaiter = next;
                    if (next == null)
                        lastWaiter = trail;
                }
                else
                    trail = t;
                t = next;
            }
        }
.................
}

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

 public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
 
 
/**
         * Removes and transfers nodes until hit non-cancelled one or
         * null. Split out from signal in part to encourage compilers
         * to inline the case of no waiters.
         * @param first (non-null) the first node on condition queue
         */
        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
 
        final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
 
        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

 

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

public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

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

 public final void awaitUninterruptibly() {
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            boolean interrupted = false;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if (Thread.interrupted())
                    interrupted = true;
            }
            if (acquireQueued(node, savedState) || interrupted)
                selfInterrupt();
        }

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

public final boolean await(long time, TimeUnit unit)
                throws InterruptedException {
            if (unit == null)
                throw new NullPointerException();
            long nanosTimeout = unit.toNanos(time);
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            long lastTime = System.nanoTime();
            boolean timedout = false;
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                if (nanosTimeout <= 0L) {
                    timedout = transferAfterCancelledWait(node);
                    break;
                }
                if (nanosTimeout >= spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
                long now = System.nanoTime();
                nanosTimeout -= now - lastTime;
                lastTime = now;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null)
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
            return !timedout;
        }

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

private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;
 
    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));
 
        } catch (Exception ex) { throw new Error(ex); }
    }

 这篇介绍了AQS的基本情况,重点介绍了Node类和ConditoinObject类。下一篇会介绍AQS自身的重要方法。

发布了169 篇原创文章 · 获赞 6 · 访问量 3489

猜你喜欢

转载自blog.csdn.net/weixin_42073629/article/details/104788255