Java JUC并发 AbstractQueuedSynchronizer学习

前言

java.util.concurrent包(简称JUC)相比大家都应该很熟悉了。JUC包含许多线程安全、测试良好、高性能的并发构建块,之前一直没有研究过其核心锁的机制。前些日子,使用到了countDownLatch,没事点开了其源码,发现其核心功能是由AbstractQueuedSynchronizer的一个内部子类Syn实现的,进而发现在JUC中如ReentrantLock等很多常用类都实现了AbstractQueuedSynchronizer,AbstractQueuedSynchronizer为其实现了并发控制的功能。下面就详细分析一下AbstractQueuedSynchronizer。

AbstractQueuedSynchronizer 分析

AbstractQueuedSynchronizer 源码实现推荐去看
深度解析Java8 – AbstractQueuedSynchronizer的实现分析(上)
深度解析Java8 – AbstractQueuedSynchronizer的实现分析(下)

这片博客已经写的十分详细明了,珠玉在前就不再赘述了。我画了一下ReentrantLock 获取锁和释放锁的流程图,会放在这篇文章最后,希望可以在AQS看源码的时候有所帮助。在这里就之讲述一下我理解的AQS。

AbstractQueuedSynchronizer(简称AQS)核心思想就是通过一个共享变量同步状态,通过队列管理阻塞线程。
其中AQS中状态变量由各个子类定义管理,AQS重点提供队列的管理,线程的唤醒和挂起。

状态变量

AQS 使用一个 volatile修饰的int型变表示。一般情况下,状态变量如果为0,则表示锁尚未被获取到。在CountDownLatch 值为构造方法传入的数值,当countDown调用是,状态就会减一,直到状态变量为0,则唤醒阻塞队列中的线程。而ReentrantLock中由于是互斥锁,仅有0,1取值,表示锁是否已经获取:

  /** * Returns the current value of synchronization state. * This operation has memory semantics of a {@code volatile} read. * @return current state value */
    protected final int getState() {
        return state;
    }

    /** * Sets the value of synchronization state. * This operation has memory semantics of a {@code volatile} write. * @param newState the new state value */
    protected final void setState(int newState) {
        state = newState;
    }

    /** * Atomically sets synchronization state to the given updated * value if the current state value equals the expected value. * This operation has memory semantics of a {@code volatile} read * and write. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that the actual * value was not equal to the expected value. */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

compareAndSetState是java Unsafe提供的CAS方法,保证了其安全性。在线程获取锁时,就会尝试使用这个方法更新状态位。

阻塞线程队列

前面说过,没有获取到锁的线程就会被放入一个队列中。AQS内部维护着一个FIFO的CLH队列,所以AQS并不支持基于优先级的同步策略。队列中的每个线程结点只要等待其前继释放锁就可以了。在这个对列中head节点时一个空节点。
《Java JUC并发 AbstractQueuedSynchronizer学习》

Node节点中还有一个waitStatus的变量,用来描述节点的状态。

AQS的队列中,在有并发时,肯定会存取一定数量的节点,每个节点[G4] 代表了一个线程的状态,有的线程可能“等不及”获取锁了,需要放弃竞争,退出队列,有的线程在等待一些条件满足,满足后才恢复执行(这里的描述很像某个J.U.C包下的工具类,ReentrankLock的Condition,事实上,Condition同样也是AQS的子类)等等,总之,各个线程有各个线程的状态,但总需要一个变量来描述它。
waitStatus由四个取值:

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

表示了:节点取消,节点等待触发,节点等待条件,节点状态需要向后传播。只有前一个节点时SIGNAL的当前节点才会被挂起。

线程的阻塞/唤醒

AQS并没有使用java线程提供的wait/notify 阻塞/唤醒线程,它通过了LockSupport.park() 和 LockSupport.unpark() 的本地方法来实现线程的阻塞和唤醒。实际上这两个方法也是对java Unsafe的一个封装。Unsafe之后有机会在讲。

独占锁的获取和释放流程

获取

《Java JUC并发 AbstractQueuedSynchronizer学习》

释放

《Java JUC并发 AbstractQueuedSynchronizer学习》

    原文作者:JUC
    原文地址: https://blog.csdn.net/wsm0712syb/article/details/57086222
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞