Java JUC并发 AbstractQueuedSynchronizer学习

来源:互联网 发布:江恩轮软件下载 编辑:程序博客网 时间:2024/06/04 19:47

前言

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节点时一个空节点。
这里写图片描述

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之后有机会在讲。

独占锁的获取和释放流程

获取

独占锁的获取流程

释放

这里写图片描述

0 0