• 抽象同步队列AQS(上)—— 宏观上理解AQS


      这一切都是命运石之门的选择!

      1.AQS在jdk中的应用架构

      打开idea,找到AbstractQueuedSynchronizer类,按住ctrl+alt+shift+u,显示如图:

       然后选中AbstractQueuedSynchronizer,然后按ctrl+t,显示出aqs的应用:

       然后全选,回车:

       AQS在jdk的应用架构就是上面的结构。

      2.AQS的特性

      1:阻塞等待队列

      2:共享/独占模式

      3:支持公平/非公平加锁

      4:可重入

      5:可以中断

      

      3.AQS的重要属性介绍

      先来看AbstractQueuedSynchronizer的父类AbstractOwnableSynchronizer,打开AbstractOwnableSynchronizer,然后按alt+7:

       黄色的为类的成员变量,红色的是方法;

        exclusiveOwnerThread:独占模式下获取锁的线程;

      我们接着看AQS的构成,一样的alt+7:

      内部类Node:阻塞队列的实现,里面核心属性:(一个节点绑定一个线程,后续节点都代表一个线程)

        SHARED:标记节点未共享模式,默认值为一个新的节点;

        EXCLUSIVE:标记节点为独占模式,默认值为null;

        CANCELLED:节点等待标记,如果waitStatus为这个值(1),表示线程已经被取消;(1,代表线程已经出现异常,可能是中断引起的异常,或者是程序本身的错误引起的,节点状态标记为1会被废弃掉)

        SIGNAL:如果waitStatus为这个值(-1),通知后续节点继续运行标记;(-1,代表线程可以被唤醒)

        CONDITION:如果waitStatus为这个值(-2),该节点会从等待队列中转移到同步队列中;(-2,条件等待)

        PROPAGATE:如果waitStatus为这个值(-3),表示下一次共享式同步状态获取将会被无条件地传播下去;(-3,代表线程是可传播的)

         waitStatus:被volatile修饰,变更以后别的线程可以及时可见,其中值为上面4个加上默认值0(1,0,‐1,‐2,‐3)的5种状态,使用CAS更改状态保证这个等待状态的原子性;

        prev:前驱节点;

        next:后置节点;

        thread:节点绑定的线程;

        nextWaiter:等待队列中的后继节点,如果当前节点是共享的,那么这个字段是一个SHARED常量,也就是说节点类型(独占和共享)和等待队列中的后继节点共用同一个字段。

      Node方法:

        isShared():判断节点是不是共享模式;

        predecessor():返回前驱节点;

        Node():默认构造函数;

        Node(Thread thread, Node mode):绑定线程和下一个等待节点的构造函数;

        Node(Thread thread, int waitStatus):绑定线程和等待状态的构造函数;

      继续往下看:

       内部类ConditionObject,节点等待条件类,暂时先跳过,方法也暂时先跳过,后面会讲到,继续往下;

       重点属性来了:

        head:同步队列的头节点;

        tail:同步队列的尾节点;

        state:加锁成功标记(具体由实现类来确定);

        unsafe:cas的操作类;

        stateOffset/headOffset/tailOffset/waitStatusOffset/nextOffset:对象属性的偏移量;(用于安全的访问对象属性,用于cas操作对象的指定字段)

      4.AQS的同步实现原理

      线程的自旋+cas操作加锁成功+队列:

    lock();
    
    while(true){//自旋操作
        if (加锁成功,cas操作) {
            break;//结束自旋
        }
        head.next = new Node(Thread.currentThread(),0);//加入到等待队列
        LockSupport.park();//阻塞线程
    }
    
    业务代码;
    
    unlock();
         Thread t = queued.take();//从队列中获取线程
      LockSupport.unpark(t);//取消线程阻塞,让后续节点去竞争锁

      

      吾王剑锋所指,我等心之所向!

  • 相关阅读:
    Principle of Computing (Python)学习笔记(5) BFS Searching + Zombie Apocalypse
    《Java从入门到精通》src9-25
    《Java从入门到精通》src0-8
    windows查看某个端口被谁占用
    css selector: xpath:
    awk 正则表达式
    Centos系统各种日志存详解
    mysql日志文件
    mysql主键设置成auto_increment时,进行并发性能測试出现主键反复Duplicate entry 'xxx' for key 'PRIMARY'
    递归函数时间复杂度分析
  • 原文地址:https://www.cnblogs.com/ghsy/p/14231432.html
Copyright © 2020-2023  润新知