AQS是什么?

  AQS全称叫AbstractQueuedSynchronizer,顾名思义,抽象的队列同步装置,在java中是一个抽象类。java JUC包下常用的同步类都是通过继承AQS实现的,那么AQS到底是怎么实现的呢,

又为什么说AQS=volatile+CAS,我们通过分析ReentrantLock类的源码来一步步解开谜底。

  我们从初始化ReentrantLock开始

  1. /**
  2. * Creates an instance of {@code ReentrantLock}.
  3. * This is equivalent to using {@code ReentrantLock(false)}.
  4. */
  5. public ReentrantLock() {
  6. sync = new NonfairSync();
  7. }
  8.  
  9. /**
  10. * Creates an instance of {@code ReentrantLock} with the
  11. * given fairness policy.
  12. *
  13. * @param fair {@code true} if this lock should use a fair ordering policy
  14. */
  15. public ReentrantLock(boolean fair) {
  16. sync = fair ? new FairSync() : new NonfairSync();
  17. }

  可以看到ReentrantLock有两个构造方法,看带参数的注释可以知道如果传true,则是公平锁,这里我们使用无参构造,所以我们走的是上面一个构造方法。

所以我们调用的就是NonfairSync(非公平锁)的lock方法,如下图:

  1. /**
  2. * Sync object for non-fair locks
  3. */
  4. static final class NonfairSync extends Sync {
  5. private static final long serialVersionUID = 7316153563782823691L;
  6.  
  7. /**
  8. * Performs lock. Try immediate barge, backing up to normal
  9. * acquire on failure.
  10. */
  11. final void lock() {
  12. if (compareAndSetState(0, 1))
  13. setExclusiveOwnerThread(Thread.currentThread());
  14. else
  15. acquire(1);
  16. }
  17.  
  18. protected final boolean tryAcquire(int acquires) {
  19. return nonfairTryAcquire(acquires);
  20. }
  21. }

首先来分析compareAndSetState方法到底干了啥,进入该方法;

  1. protected final boolean compareAndSetState(int expect, int update) {
  2. // See below for intrinsics setup to support this
  3. return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
  4. }

其实从名字上我们也可以看出这个是一个典型的CAS操作,看到unsafe就没必要往下看了,因为unsafe里面的方法被java设计者封装了,我们是看不了的。

可以看到这里是想对stateOffset这个属性进行CAS操作,找到stateOffset,发现最终指向的是state属性,

再找到state属性:

发现该属性是通过volatile修饰的一个int值,暂时我们还不知道这个属性有什么作用。再回到lock方法

假设我们是第一个线程第一次进行锁操作,那么这期间肯定没其他线程会去修改state的值,则肯定会更新成功,此时该方法返回true,并将state设置为1。

返回true之后就会执行setExclusiveOwnerThread方法,该方法表示将当前线程设置为独占访问。此时,第一个线程加锁的步骤就完了,然后就执行业务

代码,执行完再调用onlock方法解锁。当然这不是我们关注的重点,我们的重点是,当有第二个,第三个线程调用lock方法的时候,我们该怎么去保证同步。

继续回到代码,当第二给线程调用lock方法的时候,假设此时第一个线程还没有释放锁,那么一定会进入else的acquire方法:

先来看tryAcquire()这个方法,顾名思义,尝试去获得锁,tryAcquire实际调用的是nonfairTryAcquire() 方法,因为我们看的非公平锁。

代码如下:

这个方法比较简单,如果state等于0也就是没拿到锁,就用cas去尝试拿,拿到就返回true,如果

在当前线程中,如果说当前线程和锁的所有者是同一个线程,则把sate+1,表示锁的重入。

再来看acquireQueued(addWaiter(Node.EXCLUSIVE), arg),这个方法就不去细看了,大致 的意思

就是通过cas实现往队列里面放入等待的线程,当执行的线程释放锁的时候,该队列的线程会相互竞争

直到某一个线程拿到锁。

总结:

  1. 调用自定义同步器的tryAcquire()尝试直接去获取资源,如果成功则直接返回;
  2. 没成功,则addWaiter()将该线程加入等待队列的尾部,并标记为独占模式;
  3. acquireQueued()使线程在等待队列中休息,有机会时(轮到自己,会被unpark())会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
  4. 如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。

由于此函数是重中之重,我再用流程图总结一下:

至此,acquire()的流程终于算是告一段落了。这也就是ReentrantLock.lock()的流程,不信你去看其lock()源码吧,整个函数就是一条acquire(1)!!!

参考:https://www.cnblogs.com/waterystone/p/4920797.html(如果需要详细的了解aqs的源码实现,请看这篇文章)

谈谈AQS的更多相关文章

  1. 美团Java面试154道题

    Java集合22题 ArrayList 和 Vector 的区别.ArrayList与Vector区别 说说 ArrayList,Vector, LinkedList 的存储性能和特性.ArrayLi ...

  2. 阿里Java架构师面试高频300题:集合+JVM+Redis+并发+算法+框架等

    前言 在过2个月即将进入9月了,然而面对今年的大环境而言,跳槽成功的难度比往年高了很多,很明显的感受就是:对于今年的java开发朋友跳槽面试,无论一面还是二面,都开始考验一个Java程序员的技术功底和 ...

  3. 350道面试题分享,拿下京东offer工资double

    350道面试题分享,拿下京东offer工资double 前言: 面试,其实是一个双向选择的过程,在这个过程里,我们不应该抱着畏惧的心态去对待,这样反而会影响自己的发挥.同时看中的应该不止薪资,还要看你 ...

  4. java 面试题目(java高级架构)

    题目信息 java基础: 1. Java 基础 JDK 和 JRE 有什么区别?   Java中JDK和JRE的区别是什么?它们的作用分别是什么? == 和 equals 的区别是什么? 两个对象的 ...

  5. 2020.4面试分享(7面收5个offer)

    都说金三银四是找工作的最佳时节,由于本人的个人职业规划跟目前工作内容不太相符(具体原因就不透露了,领导平时也要来这里逛,哈哈),四月份挑选了10多家公司投递简历(公司规模从几十人到上万人都有),参加了 ...

  6. 2020.4面试分享(7面收割5个offer)

    都说金三银四是找工作的最佳时节,由于本人的个人职业规划跟目前工作内容不太相符(具体原因就不透露了,领导平时也要来这里逛,哈哈),四月份挑选了10多家公司投递简历(公司规模从几十人到上万人都有),参加了 ...

  7. 美团 Java 面试 154 道题分享!

    Java集合22题 ArrayList 和 Vector 的区别. 说说 ArrayList,Vector, LinkedList 的存储性能和特性. 快速失败 (fail-fast) 和安全失败 ( ...

  8. Java面试,面试题

    Java面试,面试题 HashMap,HashTable,ConcurrentHash的共同点和区别 HashMap HashTable ConcurrentHashMap ArrayList和Lin ...

  9. 【面试普通人VS高手系列】谈谈你对AQS的理解

    AQS是AbstractQueuedSynchronizer的简称,是并发编程中比较核心的组件. 在很多大厂的面试中,面试官对于并发编程的考核要求相对较高,简单来说,如果你不懂并发编程,那么你很难通过 ...

随机推荐

  1. 多测师讲解pthon_re模块_高级讲师肖sir

    #import re   一.我们就re模块(也叫正则模块)介绍: 实现一个编译查找,一般在日志处理或者文件处理时用的比较多 正则表达式主要用于模式匹配和替换工作.     预定义字符集匹配: \d: ...

  2. Django的安装和项目的启动

    一.安装(安装最新LTS版): 1.命令行安装 pip install django==1.11.18 -i 源 2.pycharm 安装    二.创建项目 1.命令行创建 下面的命令创建了一个名为 ...

  3. BUUCTF-misc九连环 详解

    这个没什么玄学,我们解压出来一张照片,放到hxd中搂一眼,最后结尾的那几行中看到了zip压缩包的结尾标识符,难道这里面还有压缩包,于是我们就formostlrb 果然有图片有压缩包 我们打开压缩包看到 ...

  4. centos7安装kafka 转

    CentOS7安装和使用kafka         环境准备 安装kafka之前我们需要做一些环境的准备 1.centOS7系统环境 2.jdk环境 3.可用的zookeeper集群服务 安装jdk ...

  5. Java 8 中的抽象类和接口到底有啥区别?

    上一篇栈长发了这篇<Java 8 有多牛逼?打破一切你对接口的认知!>,帮助许多人解开了疑惑,还有读者留言说两者还有啥区别,故引发了此篇: 在我们面试时也会经常遇到面试官问抽象类和接口的区 ...

  6. DateDiff() 方法语法 T-SQL语法

    表达式DateDiff(timeinterval,date1,date2 [, firstdayofweek [, firstweekofyear]]) 允许数据类型: timeinterval 表示 ...

  7. 第五章 Linux操作系统关机、重启、注销及其查看ip命令

    一.更新系统时间与网络时间同步 1.  安装ntpdate工具 # yum -y install ntp ntpdate 2.  设置系统时间与网络时间同步 # ntpdate cn.pool.ntp ...

  8. Vue slot插槽通俗解释

    slot内容分发是Vue的Api来源 <div id="app"> <my-list> {{msg}} </my-list> </div& ...

  9. 原生JS实现动态折线图

    原生JS实现动态折线图 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> & ...

  10. eclipse 包与子包的视图显示方式切换

    上图Package Presentation ---> Hierarchical(如下图显示父包与子包) 参考:https://zhidao.baidu.com/question/2205086 ...