• AQS源码解析 2.简介 & 内部核心结构


    AQS源码解析—简介 & 内部核心结构

    AQS内部结构

    简介

    • AbstractQueuedSynchronizer:AQS,抽象队列同步器。主要是为了解决线程锁竞争的问题。

    • AQS 原理图如下,其本质是一个双向链表/队列

      image-20221024114744738

    • 多线程抢锁内部数据结构,如下

      image-20221024114755398

    核心内部类Node

        static final class Node {
            
            // 枚举:共享模式
            static final Node SHARED = new Node();
            
            // 枚举:独占模式
            static final Node EXCLUSIVE = null;
    		
            /*
             * 下面的这几个int类型的常量表示节点的状态值
             */
            
            // 表示当前节点处于取消状态
            static final int CANCELLED =  1;
            
            // 表示当前节点需要唤醒它的后继节点,(signal表示的其实是后继节点的状态,需要当前节点去唤醒它...)
            static final int SIGNAL    = -1;
            
            // ReentrantLock没有用到 等待队列(Condition)中的节点状态为 -2
            static final int CONDITION = -2;
        	
            // ReentrantLock没有用到 只在CountDownLatch中的doReleaseShared这个方法中会切换到这个状态
            static final int PROPAGATE = -3;
      
            /*
             * 表示node的状态,可选值(0, SIHGNAL(-1), CANCLLED(1), CONDITION(-2), PROPAGATE(-3)) ReentrantLock中只用到了前三个值
             * waitStatus = 0 默认状态
             * waitStatus > 0 取消状态
             * waitStatus = -1 表示当前Node如果是head节点时 释放锁之后需要唤醒后继节点
             */
            volatile int waitStatus;
    		
            /*
             * 因为node需要构建成 fifo 队列,所以需要当前节点的前继节点和后继节点
             */
            // Node的前继节点
            volatile Node prev;
    
            // Node的后继节点
            volatile Node next;
            
            // Node内部封装的线程
            volatile Thread thread;
            
            // ReentrantLock没有用到,在Condition条件队列中使用。
            Node nextWaiter;
    
            // 是否是共享模式
            final boolean isShared() {
                return nextWaiter == SHARED;
            }
    
            /*
             * 判断当前节点是有有前驱节点,有的话返回,没有的话则抛出异常
             */
            final Node predecessor() throws NullPointerException {
                Node p = prev;
                if (p == null)
                    throw new NullPointerException();
                else
                    return p;
            }
    
            // 建立初始头部或SHARED标记
            Node() { 
            }
    		
            // addWaiter使用的构造方法
            Node(Thread thread, Node mode) {
                // 把共享模式还是互斥模式存储到nextWaiter这个字段里面了
                this.nextWaiter = mode;
                this.thread = thread;
            }
    		
            // addConditionWaiter使用的构造方法
            Node(Thread thread, int waitStatus) {
                // 等待的状态,在Condition中使用
                this.waitStatus = waitStatus;
                this.thread = thread;
            }     
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    双向链表结构,节点中保存着当前线程、前一个节点、后一个节点以及线程的状态等信息。

    核心属性

     	// 队列头结点,任何时刻头结点对应的线程都是当前持锁线程
    	private transient volatile Node head;
    
        // 阻塞队列的尾节点(阻塞队列不包含 头结点,head.next -> tail 认为是阻塞队列)
        private transient volatile Node tail;
    	
        /*
         * 核心属性:表示资源
         * 独占模式下:0表示未加锁,>0表示加锁状态
         */
        private volatile int state;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

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

        // 获取Unsafe类的实例,注意这种方式仅限于jdk自己使用,普通用户是无法这样调用的
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        // 状态变量state的偏移量
        private static final long stateOffset;
        // 头节点的偏移量
        private static final long headOffset;
        // 尾节点的偏移量
        private static final long tailOffset;
        // 等待状态的偏移量(Node的属性)
        private static final long waitStatusOffset;
        // 下一个节点的偏移量(Node的属性)
        private static final long nextOffset;
    
        static {
            try {
                // 获取state的偏移量
                stateOffset = unsafe.objectFieldOffset
                    (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
                // 获取head的偏移量
                headOffset = unsafe.objectFieldOffset
                    (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
                // 获取tail的偏移量
                tailOffset = unsafe.objectFieldOffset
                    (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
                // 获取waitStatus的偏移量
                waitStatusOffset = unsafe.objectFieldOffset
                    (Node.class.getDeclaredField("waitStatus"));
                // 获取next的偏移量
                nextOffset = unsafe.objectFieldOffset
                    (Node.class.getDeclaredField("next"));
    
            } catch (Exception ex) { throw new Error(ex); }
        }
    
        // 调用Unsafe的方法原子更新state
        protected final boolean compareAndSetState(int expect, int update) {
            return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    父类属性

    AQS 还用到了其父类 AbstractOwnableSynchronizer 的一些属性:

        /*
         * 继承父类的属性
         * 独占模式下:表示当前持有锁的线程
         */
        private transient Thread exclusiveOwnerThread;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    参考

  • 相关阅读:
    小侃设计模式(九)-组合模式
    juc继承图片
    电磁兼容01 EMC概论
    【手把手教你写服务器】监听端口功能的实现、epoll技术概述
    PID--位置型PID和增量式PID比较
    基于SSM实现微博系统
    SQL Server - 提高服务器安全性13招
    API商品数据接口:实现电子商务应用程序的核心功能
    蓝桥杯(迷宫,C++)
    【自然语言处理三-自注意self attention】
  • 原文地址:https://blog.csdn.net/weixin_53407527/article/details/127948746