谈谈AQS
AQS是什么?
AQS全称叫AbstractQueuedSynchronizer,顾名思义,抽象的队列同步装置,在java中是一个抽象类。java JUC包下常用的同步类都是通过继承AQS实现的,那么AQS到底是怎么实现的呢,
又为什么说AQS=volatile+CAS,我们通过分析ReentrantLock类的源码来一步步解开谜底。
我们从初始化ReentrantLock开始
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
} /**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可以看到ReentrantLock有两个构造方法,看带参数的注释可以知道如果传true,则是公平锁,这里我们使用无参构造,所以我们走的是上面一个构造方法。
所以我们调用的就是NonfairSync(非公平锁)的lock方法,如下图:
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L; /**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
} protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
首先来分析compareAndSetState方法到底干了啥,进入该方法;
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
其实从名字上我们也可以看出这个是一个典型的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实现往队列里面放入等待的线程,当执行的线程释放锁的时候,该队列的线程会相互竞争
直到某一个线程拿到锁。
总结:
- 调用自定义同步器的tryAcquire()尝试直接去获取资源,如果成功则直接返回;
- 没成功,则addWaiter()将该线程加入等待队列的尾部,并标记为独占模式;
- acquireQueued()使线程在等待队列中休息,有机会时(轮到自己,会被unpark())会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
- 如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。
由于此函数是重中之重,我再用流程图总结一下:

至此,acquire()的流程终于算是告一段落了。这也就是ReentrantLock.lock()的流程,不信你去看其lock()源码吧,整个函数就是一条acquire(1)!!!
参考:https://www.cnblogs.com/waterystone/p/4920797.html(如果需要详细的了解aqs的源码实现,请看这篇文章)
谈谈AQS的更多相关文章
- 美团Java面试154道题
Java集合22题 ArrayList 和 Vector 的区别.ArrayList与Vector区别 说说 ArrayList,Vector, LinkedList 的存储性能和特性.ArrayLi ...
- 阿里Java架构师面试高频300题:集合+JVM+Redis+并发+算法+框架等
前言 在过2个月即将进入9月了,然而面对今年的大环境而言,跳槽成功的难度比往年高了很多,很明显的感受就是:对于今年的java开发朋友跳槽面试,无论一面还是二面,都开始考验一个Java程序员的技术功底和 ...
- 350道面试题分享,拿下京东offer工资double
350道面试题分享,拿下京东offer工资double 前言: 面试,其实是一个双向选择的过程,在这个过程里,我们不应该抱着畏惧的心态去对待,这样反而会影响自己的发挥.同时看中的应该不止薪资,还要看你 ...
- java 面试题目(java高级架构)
题目信息 java基础: 1. Java 基础 JDK 和 JRE 有什么区别? Java中JDK和JRE的区别是什么?它们的作用分别是什么? == 和 equals 的区别是什么? 两个对象的 ...
- 2020.4面试分享(7面收5个offer)
都说金三银四是找工作的最佳时节,由于本人的个人职业规划跟目前工作内容不太相符(具体原因就不透露了,领导平时也要来这里逛,哈哈),四月份挑选了10多家公司投递简历(公司规模从几十人到上万人都有),参加了 ...
- 2020.4面试分享(7面收割5个offer)
都说金三银四是找工作的最佳时节,由于本人的个人职业规划跟目前工作内容不太相符(具体原因就不透露了,领导平时也要来这里逛,哈哈),四月份挑选了10多家公司投递简历(公司规模从几十人到上万人都有),参加了 ...
- 美团 Java 面试 154 道题分享!
Java集合22题 ArrayList 和 Vector 的区别. 说说 ArrayList,Vector, LinkedList 的存储性能和特性. 快速失败 (fail-fast) 和安全失败 ( ...
- Java面试,面试题
Java面试,面试题 HashMap,HashTable,ConcurrentHash的共同点和区别 HashMap HashTable ConcurrentHashMap ArrayList和Lin ...
- 【面试普通人VS高手系列】谈谈你对AQS的理解
AQS是AbstractQueuedSynchronizer的简称,是并发编程中比较核心的组件. 在很多大厂的面试中,面试官对于并发编程的考核要求相对较高,简单来说,如果你不懂并发编程,那么你很难通过 ...
随机推荐
- mysql物理优化器代价模型分析【原创】
1. 引言 mysql的sql server在根据where condition检索数据的时候,一般会有多种数据检索的方法,其会根据各种数据检索方法代价的大小,选择代价最小的那个数据检索方法. 比如说 ...
- kafka生产者 消费者
publisher.php <?php $rk = new RdKafka\Producer(); $rk->addBrokers("192.168.33.50"); ...
- spring boot:使用async异步线程池发送注册邮件(spring boot 2.3.1)
一,为什么要使用async异步线程池? 1,在生产环境中,有一些需要延时处理的业务场景: 例如:发送电子邮件, 给手机发短信验证码 大数据量的查询统计 远程抓取数据等 这些场景占用时间较长,而用户又没 ...
- python操作excel xlwt (转)
Python中xlrd和xlwt模块使用方法 阅读目录 安装 xlrd模块使用 xlwt模块 xlrd模块实现对excel文件内容读取,xlwt模块实现对excel文件的写入. 回到顶部 安装 ? ...
- 教你怎么写jQuery的插件
jQuery(以下简称JQ)是一个功能强大而又小巧的JS框架,现在很多网站都在使用JQ,本站也不例外.本文教大家如何写一个属于你自己的JQ插件. 本JQ插件例子是在你网站的文章结尾处添加你的版权. J ...
- sql server 2008 r2 直接下载地址,可用迅雷下载
sqlserver 2008 r2 直接下载地址,可用迅雷下载 下载sqlserver 2008 r2 ,微软用了一个下载器,经过从下载器上,将他的地址全部用键盘敲了下来.最终的简体中文版地址如下: ...
- js和vue方法的相互调用(iframe父子页面的方法相互调用)。
项目是前后端不分离的,模板引擎使用的JSP. 但是使用了Vue+ElementUI,这里列举一些常用的调用方式,有时候可能. 在js里调用vue方法 我们需要把方法注册到vue对象之外的页面,所以对与 ...
- Monitor Ctrl-Break线程,有点坑
Monitor Ctrl-Break线程这个在idea中特有的线程,你了解吗?这线程可能会在你调试的时候给你带来谜一样的结果,为什么呢?请看下面的例子: 首先我们先复习一下多线程的状态(因为这个问题是 ...
- ScheduledExecutor定时器
为了弥补Timer 的上述缺陷,在Java 5的时候推出了基于线程池设计的 ScheduledExecutor.其设计思想是:每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互 ...
- Centos 7 firewall 命令
Centos 7 firewall 命令: 查看已经开放的端口: firewall-cmd --list-ports 开启端口 firewall-cmd --zone=public --add-por ...