对Java并发编程的一些重要知识点做记录和补充。
1.AQS是什么
AQS是JUC包下的一个抽象类,本身并没有什么具体的业务实现但是其为为Java的并发工具提供了可实现的方法,ReentrantLock,ThreadPoolExecutor等工具类都是基于AQS去实现的;
2.AQS的核心内容
三个核心属性:
state属性:表示锁资源的占有情况,如果State为0表明当前没有线程占用锁资源,如果State>0表明有线程在占用锁资源.
同步队列:当某个线程获取到锁资源了,其他的线程再想获取锁资源时会发现无法获取,这时候,线程会包装成为一个node,将其放在同步队列中。值得注意的是:同步队列使用的是双向链表的形式存储线程。
单向链表:当持有锁资源的线程被wait()方法或者await()方法停掉的时候,线程将会被暂时存在单向链表里面等待被唤醒
3.ReentrantLock和AQS的继承关系
ReentrantLock并非是直接继承AQS并实现里面的方法的,而是由里面的Sync类来继承AQS,并且,并且里面还划分了NonfairSync和FairSync两个Sync子静态内部类来分别对公平锁和非公平锁做逻辑实现。
4.公平和非公平锁的直观体现
这里有一个误区:非公平锁不会去排队。其实在ReentrantLock中,不管是公平锁还是非公平锁,只要前期没有机会拿到锁,最后都会去排队。
区别主要体现在以下两个方法不同:
lock方法:
·非公平锁:直接先抢一手锁资源,没有抢到,执行后面的tryAcquire方法
·公平锁:不抢锁,直接执行tryAcquire方法
tryAcquire方法:
·非公平锁:进来先看看锁资源有没有被占用(贼心不死),没有再抢一手,抢不到去排队
·公平锁:进来先看看锁资源有没有被占用,没有的话再看看有没有人在排队,没人的话抢一手,抢不到去排队
5.AQS的Aquire的底层逻辑
AQS里面的Aquire最主要就是调用一下以下方法:
1.tryAquire方法:抢锁的逻辑方法
2.addWaiter方法:没拿到锁资源去排队的逻辑
3.acquireQueued:排队后重新去抢锁的逻辑
6.AQS的tryAquire的底层逻辑
非公平锁的实现整体就两个核心逻辑:
1.如果state为0,直接通过CAS抢锁
2.如果state不为0,判断一下自己是不是已经持有锁了(重入锁)
公平锁和非公平锁几乎逻辑一样,唯一不一样的是要看一眼有没有人在排队以及自己是不是已经排到第一个了
7.AQS的addWaiter的底层逻辑
首先把线程加入同步队列的过程就是普通双向链表后端插入逻辑,唯一要注意的是头节点是哨兵不承担业务功能,其他的都一样,在此不再赘述
8.AQS排队后如何重新获取资源(acquireQueued)
先判断自己是不是头节点(除了哨兵节点),如果是,就去抢锁,如果不是就挂起。抢锁如果成功了就把自己置为哨兵节点,抢锁失败了就把自己挂起。挂起时要把前面节点的ws置为-1,置为-1前面的线程就会记得唤醒,否则就会饥饿。(如果前面的节点ws是1就再找前面的,因为如果前面节点ws是1说明其放弃抢锁了,马上要离开队列,自然不会唤醒后面线程)