首页 > 其他分享 >聊聊JUC包下的底层支撑类-AbstractQueuedSynchronizer(AQS)

聊聊JUC包下的底层支撑类-AbstractQueuedSynchronizer(AQS)

时间:2023-02-07 11:46:02浏览次数:38  
标签:Node JUC AQS int AbstractQueuedSynchronizer state 源码 线程

聊聊JUC包下的底层支撑类-AbstractQueuedSynchronizer(AQS)

juc包下的一堆并发工具类是我们日常开发特别是面试中常被拿来问的八股文之一,为了工作也好,为了面试也罢,今天开始想尝试着把这些给大伙描述明白,所以开始写下这篇博文,如果后续要涉及每个常用类的源码的话可能会是一个系列,计划从比较底层的AQS聊起,然后结合ReentrantLock的源码来聊AQS独占锁的具体实现,以及加锁和释放锁的过程;然后再聊聊JUC包下其他类如CountDownLatch、CyclicBarrier、Phaser、ReadWriteLock、Semaphore、Exchanger以及LockSupport的使用和原理,有时间的话再结合CountDownLatch的源码来聊AQS共享锁的具体实现,接下来话不多说开始踏上揭秘AQS之旅

一、AQS是什么

  • AQS全称AbstractQueuedSynchronizer即抽象队列同步器,可以理解成是一个可以实现锁的框架(基类),它可以实现共享和独占两种模式的锁,事实上juc包下很多关于锁的工具类也是基于AQS的;它提供了一些模板方法供子类实现拓展,并且本身结合底层的Unsafe类实现了基于cpu原语层的安全操作,从而实现在并发环境下的线程安全

二、AQS的实现原理

  • AQS能作为基类来实现锁的功能主要原因来自于它维护的一个int类型的state变量和一个FIFO的双向队列;实现类可以根据自身的需求,通过控制state的值来决定线程是否需要阻塞,而双向队列用来存放没有争抢到锁资源的线程;并且AQS通过结合Unsafe类的能力封装了可以线程安全的操作state值的方法(一堆CAS的操作方法),这样程序员就可以只关注锁的使用而不必关注底层实现的细节了;
  • AQS支持两种模式锁的实现,分别是独占锁和共享锁,独占锁的具体实现以ReentrantLock为代表,共享锁的实现诸如CountDownLatch、CyclicBarrier等

注:由于后续会介绍AQS的源码以及子类实现,所以这里只是大白话般的描述了一下AQS的原理,即两个关键:一个state一个双向队列,其实要展开还有许多细节要聊,考虑到这些细节后续源码里会有体现这里就不再表述了

三、AQS的源码简析

1、state变量

private volatile int state;

1、state是AQS提供的供子类拓展的一个同步状态,子类可以维护state的不同状态来实现不同效果的锁实现,如ReentrantLock就是通过维护state是否为0或1来表示锁的加解操作;

2、用volatile修饰主要是为了在并发环境下线程可见

2、Node内部类

  • Node类是双向CLH队列的构成元素,其维护的thread变量就是没有争抢到锁的线程,然后还维护了CLH队列的其他几个关键信息,如当前Node的前置节点(prev)、后续节点(next)等,下面贴上Node的源码
static final class Node {
    /** 表示当前节点正处于共享模式 */
    static final Node SHARED = new Node();
    /** 表示当前节点正处于独占模式 */
    static final Node EXCLUSIVE = null;

    /** waitStatus对应的值,表示线程已取消 */
    static final int CANCELLED =  1;
    /** waitStatus对应的值,指示后续线程需要取消标记*/
    static final int SIGNAL    = -1;
    /** waitStatus对应的值,指示线程正在等待condition唤醒*/
    static final int CONDITION = -2;
    /**
     * waitStatus value to indicate the next acquireShared should
     * unconditionally propagate
     */
    static final int PROPAGATE = -3;

    /**
     * 等待状态,枚举值有1,0,-1,-2,-3分别对应上面的几个变量值,0表示以上状态都不是
     */
    volatile int waitStatus;

    /**
     * 当前Node的前置节点
     */
    volatile Node prev;

    /**
     * 当前Node的后继节点
     */
    volatile Node next;

    /**
     * 与当前Node绑定的被阻塞的线程
     */
    volatile Thread thread;

    /**
     * 下一个等待节点,Condition状态下要用到
     */
    Node nextWaiter;

    /**
     * 如果节点在共享模式下等待,则返回true。
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    /**
     * 获取当前队列的前置节点
     *
     * @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;
    }
}

3、AQS供子类拓展的模板方法

独占模式供子类实现的方法

  • tryAcquire(int) 尝试获取锁,获取成功返回true,失败返回false

  • tryRelease(int) 尝试释放锁,释放成功返回true,失败返回false

共享模式供子类实现的方法

  • tryAcquireshared(int)

    尝试获取锁,负数表示失败; 0表示功,但没有剩余可用资源:正数表示成功,且有剩余资源。

  • tryReleaseshared(int)

    尝试释放锁,成功返回true,失败返回false

上面简单介绍了一下AQS的原理以及源码的部分注释,接下来我会写一篇ReentrantLock源码解读的相关文章,来体验下AQS的实际用处

标签:Node,JUC,AQS,int,AbstractQueuedSynchronizer,state,源码,线程
From: https://www.cnblogs.com/darling2047/p/17097838.html

相关文章

  • Java多线程并发06—CAS、AQS
    CAS(CompareAndSwap/Set)概念CAS函数,是比较并交换函数,它是原子操作函数。原理CAS是基于乐观锁的原理进行操作的。它总是认为自己可以成功完成操作。当多个线程同时使用CAS......
  • JUC 常用 4 大并发工具类
    什么是JUC?JUC就是java.util.concurrent包,这个包俗称JUC,里面都是解决并发问题的一些定义类,该包的位置位于java下面的rt.jar包下面。4大常用并发工具类CountDownLatchC......
  • juc 多线程 锁失效的三种情况 (多例.事务.集群)
    java自己的synchornized和lock锁都是悲观锁,默认一定有其他线程争抢并修改数据    乐观锁默认没有别的线程来抢夺,修改数据更适合读多的场景,通过version控......
  • Java并发编程——并发包中锁的AQS通用实现
    一、包结构介绍我们查看下java.util.concurrent.locks包下面,发现主要包含如下类:可以发现ReentrantLock和ReentrantReadWriteLock都是AbstractQueueSynchronizer类。我们......
  • 什么是JUC
    什么是JUC与线程和进程一、什么是JUC面试高频问javautilconcurrentjava.util工具包包、分类业务:普通的线程代码ThreadRunnable:没有返回值,效率相比Callable相对较低......
  • Java并发JUC——Future和Callable
    Runnable的缺陷不能返回一个返回值也不能抛出checkedExceptionCallable接口类似于Runnable,被其他线程执行的任务Callable接口中只有一个call()方法,和Runnable相比,......
  • Java并发JUC——AQS
    为什么需要AQS锁和协作类有共同点:闸门像ReentrantLock和Semaphore有一些共同点,并且很相似事实上,不仅仅是ReentrantLock和Semaphore,包括CountDownLatch、ReentrantReadW......
  • Java并发JUC——线程池
    前言如果不使用线程池,每个任务都需要新开一个线程处理这样开销太大,我们希望有固定数量的线程来执行任务,这样就避免了反复创建并销毁线程所带来的开销问题为什么要使用......
  • Java并发JUC——synchronized和Lock
    synchronizedsynchronized作用原子性:synchronized保证语句块内操作是原子的。可见性:synchronized保证可见性(通过“在执行unlock之前,必须先把此变量同步回主内存”实......
  • Java并发JUC——Atomic原子类
    什么是原子类原子是不可分割的最小单位,故原子类可以认为其操作都是不可分割一个操作时不可中断的,即便是在多线程的情况下也可以保证原子类的作用和锁类似,是为了保证并发......