【AQS】核心实现

文章首发于:clawhub.club


AQS(AbstractQueuedSynchronizer)是Java并发工具的基础,采用乐观锁,通过CAS与自旋轻量级的获取锁。
下面简单分析一下他的核心实现:

核心属性

锁相关属性:

 /**
     * The synchronization state.
     * 同步状态,0代表锁没有被占用,大于0代表锁被占用且表示锁被重入的次数
     */
    private volatile int state;
    /**
    *存储当前获取锁的线程,继承自AbstractOwnableSynchronizer
    */
    private transient Thread exclusiveOwnerThread;
    

同步队列相关属性:

    /**
     * 对首,只是一个标志位,不存储线程
     */
    private transient volatile Node head;

    /**
     * 队尾,新增加的等待线程入队节点
     */
    private transient volatile Node tail;

Node节点中重要属性:

    /**
    *当前节点所代表的线程
    */
    volatile Thread thread;

    // 双向链表,每个节点需要保存自己的前驱节点和后继节点的引用
    volatile Node prev;
    volatile Node next;

    // 线程所处的等待锁的状态,初始化时,该值为0
    volatile int waitStatus;
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;
    
    /**
    * 用于条件队列与共享锁
    */
    Node nextWaiter;

状态

AQS中的状态,代表锁是否被获取,如果为0,则没有被任何线程获取,如果是独占锁的话,值为1,如果是共享锁,则值为持有锁的线程的数量。
当讨论独占锁时,除了获取锁之外,还要记录一下获取到锁的线程,所以有了类AbstractOwnableSynchronizer中的exclusiveOwnerThread属性。

队列

AQS中的队列是双向链表,依赖于Node节点中的prev和next属性,队列中存储了等待获取锁的集合。队列中有head和tail节点,头节点不存储等待锁线程。
如图:


【AQS】核心实现_第1张图片
同步队列.png

CAS

以前也好好看过CAS操作,主要依赖于Unsafe类中的各种方法,保证只有一个线程能修改值,如果失败则通过for循环不断重试,直到修改成功,入等待节点入队列的操作。

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

参考:
https://segmentfault.com/a/1190000016058789

你可能感兴趣的:(【AQS】核心实现)