AQS分析

作者: LZhan | 来源:发表于2019-12-27 16:22 被阅读0次

转自 公众号 [彤哥说源码]

1 简介

AQS的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎所有的锁和同步器提供一个基础框架。
AQS是基于FIFO的队列实现的,并且内部维护了一个状态变量state,通过原子更新这个状态变量state即可以实现加锁解锁操作。

2 源码分析

    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;

        /**
         * Link to predecessor node that current node/thread relies on
         * for checking waitStatus. Assigned during enqueuing, 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 during enqueuing, adjusted
         * when bypassing cancelled predecessors, and nulled out (for
         * sake of GC) when dequeued.  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.  The next field of cancelled nodes is set to
         * point to the node itself instead of null, to make life
         * easier for isOnSyncQueue.
         */

        // 后一个节点
        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.  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;
        }
    }

可以看出AQS基于典型的双链表结构,节点中保存着当前线程、前一个节点、后一个节点以及线程的状态等信息。

主要属性

//队列的头节点
private transient volatile Node head;
//队列的尾节点
private transient volatile Node tail;
//控制加锁解锁的状态变量
private volatile long state;

定义了一个状态变量和一个队列,状态变量用来控制加锁解锁,队列用来放置等待的线程。

这几个变量都要使用volatile关键字来修饰,因为是在多线程环境下操作,要保证它们的值修改之后对其它线程立即可见。

这几个变量的修改是直接使用的Unsafe这个类来操作的。

    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
                (AbstractQueuedLongSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedLongSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedLongSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
    }

    /**
     * CAS head field. Used only by enq.
     */
    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }

    /**
     * CAS tail field. Used only by enq.
     */
    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }

    /**
     * CAS waitStatus field of a node.
     */
    private static final boolean compareAndSetWaitStatus(Node node,
                                                         int expect,
                                                         int update) {
        return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                        expect, update);
    }

    /**
     * CAS next field of a node.
     */
    private static final boolean compareAndSetNext(Node node,
                                                   Node expect,
                                                   Node update) {
        return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
    }

3 子类需要实现的主要方法

我们可以看到AQS的全称是AbstractQueuedSynchronizer,它本质上是一个抽象类,说明它本质上应该是需要子类来实现的,那么子类实现一个同步器需要实现哪些方法呢?

 // 互斥模式下使用:尝试获取锁
 protected boolean tryAcquire(long arg) {
        throw new UnsupportedOperationException();
 }
 //互斥模式下使用:尝试释放锁
 protected boolean tryRelease(long arg) {
        throw new UnsupportedOperationException();
 }
//共享模式下使用:尝试获取锁
protected long tryAcquireShared(long arg) {
        throw new UnsupportedOperationException();
}
// 共享模式下使用:尝试释放锁
protected boolean tryReleaseShared(long arg) {
        throw new UnsupportedOperationException();
}
// 如果当前线程独占着锁,返回true
protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
 }

问题:这几个方法为什么不直接定义成抽象方法呢?

因为子类只要实现这几个方法中的一部分就可以实现一个同步器了,所以不需要定义成抽象方法。

4 总结

(1)AQS是Java中几乎所有锁和同步器的一个基础框架,这里说的是“几乎”,因为有极个别确实没有通过AQS来实现;

(2)AQS中维护了一个队列,这个队列使用双链表实现,用于保存等待锁排队的线程;

(3)AQS中维护了一个状态变量,控制这个状态变量就可以实现加锁解锁操作了;

(4)基于AQS自己动手写一个锁非常简单,只需要实现AQS的几个方法即可。

相关文章

网友评论

      本文标题:AQS分析

      本文链接:https://www.haomeiwen.com/subject/exedoctx.html