深入理解AQS
- 一,AQS
- 1,ReentrantLock
- 2,CAS
- 3,AbstractQueuedSynchronizer
- 3.1,FairSync
- 3.2,NofairSync
- 3.3,AQS中几个重要的相关参数
- 3.4,Node
一,AQS
AbstractQueuedSynchronizer,定义了一套多线程访问共享资源的同步器框架,依赖于状态的同步器
1,ReentrantLock
一种基于AQS框架的应用实现,类似于synchronized是一种互斥锁,可以保证线程安全。它支持手动加锁与解锁,支持加锁的公平性。主要是Lock锁的实现
public class ReentrantLock implements Lock, Serializable
接下来可以手动的猜想一下这个reentrantLock的实现
ReentrantLock lock = new ReentrantLock(true);
HashSet hashSet = new HashSet();
3个线程 T0,T1,T2
lock.lock();
while(true){
if(加锁成功){
break;
}
hashSet.add(thread);
LockSupprot.park();
}
T0获取锁
xxxxx业务逻辑
xxxxx业务逻辑
lock.unlock();
Thread thread = hashSet.get();
LockSupport.unPark(thread);
三大核心:自旋,加锁,LockSupport,队列(LinkQueue),为了解决这个公平锁和非公平锁,因此优先考虑这个队列。
2,CAS
compare and swap,比较与交换

如在jmm模型中,两个工作内存都去修改主内存的值。主内存中存在一个a = 0,线程A和线程B的工作内存同时获取到这个值,如果线程A先修改这个值,则线程A会和主内存比较,如果线程A的值a和主内存的值一致,那么就会直接进行修改,如改成a = 1,那么线程B也要改这个值,线程B中的a = 0,那么会和主内存a比较,发现不一致,主内存a=1,那么就会优先将线程B中的值修改成a = 1,再对a进行修改。就是说相等直接修改,不相等需要重新读取,再进行修改。即在一个原子操作里面进行比较和替换
主要通过这个unsafe类实现,里面的实现也是原子类操作,主要是通过以下三个类实现。
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
3,AbstractQueuedSynchronizer
该类是ReentrantLock里面的一个抽象内部类。ctrl + alt + shift + u看所有子类,Ctrl + T,看所有的继承类,可以发现很多地方都继承或者实现了这个抽象类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4dzYpLnW-1657115375965)(C:\Users\HULOUBO\AppData\Roaming\Typora\typora-user-images\1656947493769.png)]](https://static.555519.xyz/ab62/uploads/20220710/e42c1f5ab805424f36ff0ee67b569090.jpg)
Sync是ReentrantLock的一个抽象的静态内部类,根据图也可以发现这个Sync继承了这个AQS
abstract static class Sync extends AbstractQueuedSynchronizer
通过实现Sync这个接口得到了FairSync公平锁类和NofairSync非公平锁这个类
3.1,FairSync
实现了公平锁,需要排队获取锁,如存在线程t1,t2,t3依次获取锁,需要依次排队执行,突然来了一个线程t4,也是需要排在线程t3后面
ReentrantLock reentrantLock = new ReentrantLock(true);
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
static final class FairSync extends ReentrantLock.Sync{...}
公平锁获取锁的方式如下
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
总而言之就是说,公平锁就是就是通过一个队列实现,需要进行排队的去获取锁资源。主要是通过这个state的资源状态器来控制获取锁的拥有者,如果state为0,则表示队列中的下一个线程可以去获取锁,并且通过cas的方式来保证锁的安全并发问题。通过队列的思想,来保证这个获取锁的公平性和有序性。
3.2,NofairSync
实现了非公平锁,默认为非公平锁,会存在抢锁的情况
ReentrantLock reentrantLock = new ReentrantLock(flase);
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
static final class NonfairSync extends ReentrantLock.Sync{...}
非公平锁获取锁的方法和公平锁类似,只是少了几步使用队列的几个方法
3.3,AQS中几个重要的相关参数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-12BMqIQA-1657115375967)(C:\Users\HULOUBO\AppData\Roaming\Typora\typora-user-images\1657029597278.png)]](https://static.555519.xyz/ab62/uploads/20220710/9ed94a89b15a49f35d87334d993c79a0.jpg)
exclusiveOwnerThread:用于记录当前独占模式下,获取锁的线程是谁
state:同步状态器,默认为0,表示当前没有线程获取锁,外面的线程可以来获取锁了
Node:双向链表结构,是一个同步等待队列,head:队头,tail:队尾,prev前躯指针,next,后继指针
waiteState:结点的什生命状态
Lock锁和synchronized锁都是可重入锁
3.4,Node
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zfr5p0Ru-1657115375970)(C:\Users\HULOUBO\AppData\Roaming\Typora\typora-user-images\1657110491185.png)]](https://static.555519.xyz/ab62/uploads/20220710/0030cdfec9b7a012fe83d44d9f347d70.jpg)
pre:前驱指针
next:后继指针
waitStatues:每个结点都存在很多状态,这个主要是存储结点的生命状态
结点的几个生命状态如下
SIGNAL:-1
CANCELLED:1
CONDITION:-2
PROPAGATE:-3
Init:0初始状态
结点入队顺序如下,入队时,将入队结点得前驱指针指向链表的tail结点,将tail节点的next节点指向当前节点,并将当前结点设置为tail尾指针结点。
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
当前结点前面有结点获取锁,当前节点需要进行阻塞park,线程要开始排队等待。
结点在阻塞之前,还得尝试获取一次锁。
a,如果结点可以获取到锁,即当前节点为头结点的下一个结点,头结点即将锁被释放,则把当前结点作为头结点。之前的头结点就可以被GC回收了
b,如果结点不能获取到锁,那么当前结点就要等待被唤醒
(1),第一轮循环会去修改head状态,并且将waitState修改为sinal = -1可被唤醒状态
(2),第二轮,阻塞线程
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
每个结点的生命状态的变换如下。默认的结点状态为0,需要将节点状态(waitState)转化为-1可唤醒状态。前一个节点中的waitStatus状态记录着后一个节点的生命状态。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
在修改成可被唤醒的状态之后,就进行阻塞操作。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
在头结点释放锁的时候,也会发一个通知去告知下一个需要获取锁的线程来抢锁,即唤醒队列中的下一个被阻塞的线程
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
最后在这个unparkSuccessor方法中,也有这个具体的unpark唤醒操作
if (s != null)
LockSupport.unpark(s.thread)
通过上述代码描述,也验证了一开始的猜想:自旋,加锁,LockSupport,队列(LinkQueue)