AQS--模板方法设计模式
2022-09-02 09:56:15 17 举报
自己总结的AQS,主要从设计模式、数据结构、时序图方面进行分析源代码
作者其他创作
大纲/内容
private void unparkSuccessor(Node node) { int ws = node.waitStatus; if (ws < 0)//font color=\"#ff0000\
Node
Condition await逻辑
4.2 emptyAwait中第一个节点,转移到AQS队列,准备重新抢锁
唤醒线程
先尝试加锁,加锁失败再进行入队不执行hasQueuedPredecessors()这样效率更高,可能少执行一次CPU用户态切换
waitStatus:-2
final boolean isOnSyncQueue(Node node) { if (node.waitStatus == Node.CONDITION || node.prev == null) return false; font color=\"#ff0000\
next
prev
waitStatus:0
lastWaiter
结束
signal() 唤醒await线程去AQS队列等待
Node lastWaiter //等待队列尾
head
代码实现:死循环,每2秒消费一个物品
current == getExclusiveOwnerThread()
是
//将当前线程加入到Condition的队中 private Node addConditionWaiter() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node t = lastWaiter; // 如果尾节点取消了,font color=\"#ff0000\
fasle
Node firstWaiter //等待队列头
Object[] goods 货物
false
队列中是否有其他线程正在等待加锁,从后tail 开始遍历
非公平锁实现
waitStatus:-1
2.消费者发现没有库存,进入emptyAwait等待记得用whlie循环,醒来后继续消费
状态3:线程2 再次tryAcquire获取失败,改变前驱节点状态 :-1,并搁置当前线程(线程2)
AQS 加锁过程
condition = lock.newCondition()condition.signal()
true
fullAwait
AQS 内部类 ConditionObjectawait() 释放锁,等待
OwnerThread = 线程1
ReenTranctLock可重入锁实现
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
状态1:线程1 获取锁后 await
Node@(tail)
Thread exclusiveOwnerThread//记录获取锁的线程
苏醒
Condition使用案例:生产者与消费者
unparkSuccessor(h)
font color=\"#ff0000\
//Node 状态与信号量 waitStatus = 0 ;//当前节点在sync队列中,等待着获取锁CANCELLED = 1;//当前的线程被取消SIGNAL = -1;//当前节点【哑元】的【后继节点】包含的线程需要运行,下次unpark后续线程CONDITION = -2;//当前节点在condition队列中PROPAGATE = -3;//当前场景下,后续的acquireShared能够得以执行
unlinkCancelledWaiters()
1.消费者抢先获得锁
(JDK官方案例)
tryAcquire(arg)
thread:线程3
thread:thread-1
state = 1 -> 0 -> 1
OwnerThread =线程2
将Condition中的节点,放到AQS队列中
5.消费者从新抢占到锁,被唤醒,继续消费,发现有1个库存,进行消费,然后释放锁此时完成一个完整的通路:生产、消费
LockSupport.park(this)
线程2
唤醒别人的地方-.-
返回true可重入锁结束
head != null && head.waitStatus != 0
state = 1
唤醒头节点的后续节点,头节点状态 CAS设置为 0
//大家的私有锁lock = new ReentrantLock();
NonfairSync(默认)
成功
first != null
doSignal
//消费者锁定,库存没有,锁定进行等待emptyAwait= lock.newCondition();
3.生产者获得锁
AQS 解锁过程
thread:线程2
创建锁
false,重试
2.1 释放锁,进入队列
梦开始的地方-.-
waitStatus:由0->SIGNAL
hasQueuedPredecessors()
代码
可重入锁:次数减少
备注:1.因为消费者消费速度快,所以库存一直不会满,所以生产者线程1,一直不会进入停工阶段,即不会进入fullAwait队列2.步骤5完成后,由于消费者的死循环,又会从新第一步开始,抢锁、消费、等待。开始新一轮的循环
addWaiter(null)创建一个值含当前线程的Node结点并返回这个节点
tail
thread:thread-2
生产者:线程1
Node node = addConditionWaiter()
//获取node结点的前驱结点Node p = node.predecessor();
setState(c+1)
库存数步骤1:int count = 0步骤4:int count = 1
AQS 数据结构
int state=1
return false未成功释放锁结束
int count 库存数,上限100,满了停止生产
thread:null
ConditionObject 数据结构
acquire(1) 方法 获取锁
Node@(head)
firstWaiter
与AQS队列中的队列不同:这里没有哑元节点
AbstractQueueSynchronized 模板方法 设计模式
失败
firstWaiter = first.nextWaiter
进行CAS 原子操作尝试加锁
checkInterruptWhileWaiting(node)
获取到锁,执行业务代码后,需要释放锁释放锁:lock.release()
状态2:线程2 通过 tryAcquire,加锁失败,初始化哑元节点,当前线程节点
柱塞过程中,线程被中断了?跳出循环
解锁
//生产者锁定,库存满了,进行等待消费(暂停生产),一般不会进入等待搁置fullAwait = lock.newCondition();
是否头节点
完全释放锁,调用AQS的release()包括可重入锁,重入多少次都释放掉唤醒 AQS队列中的线程
release(1) 方法 释放锁
否
释放锁,交由子类实现分别有:的可重入锁释放、读性锁释放符合模板方法设计模式
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; }
代码实现:死循环,每5秒生产一个产品
生产者锁定 的Condition队列库存满了,进行等待消费(暂停生产)
消费者锁定 的Condition队列库存没有,锁定进行等待
exclusiveOwnerThread ==null
parkAndCheckInterrupt()
Condition signal()逻辑
线程被唤醒--重试
ReentrantLock .lock(){ sync.acquire(1)}
state=state-releases state==0
fullyRelease()
AQS同步队列
个人完善代码:https://gitee.com/ZengPeng0/ProgrammingCareer/blob/master/Life/src/main/java/com/zp/self/module/level_1_问道/III_多线程/JUC/Condition案例_生产与消费.java
尝试获取锁,交由子类实现分别有:实现公平、非公平锁符合模板方法设计模式
state = state-releases
返回false获取失败结束
没有节点结束
GC
transferForSignal(first)
emptyAwait
tryAcquire(arg)队列中若有线程等待,直接进行入队
Node p = enq(node);
返回true获取到锁结束
//获取失败后,应搁置当前线程:将 pred 节点的waitStatus变为:SIGNAL=-1,//下次好unpark当前线程,font color=\"#000000\
将当前线程加入到Condition的队中
ReentrantLock聚合一个 AQS实现Sync sync 有两种实现:公平(FairSync)、非公平(NonfairSync)获取、释放锁都通过 sync 实现
null
返回false未释放锁结束
返回trues释放锁结束
LockSupport.unpark(node.thread)
volatile int state//记录是否已锁定、重入次数
公平锁 || 非公平锁 的创建ReentrantLock lock= new ReentrantLock(true);
消费者:线程2
//完全释放锁,包括可重入锁,重入多少次都释放掉 final int fullyRelease(Node node) { try { int savedState = getState(); if (release(savedState))//释放并唤醒 AQS队列线程 return savedState; //释放失败,抛异常 throw new IllegalMonitorStateException(); } catch (Throwable t) { node.waitStatus = Node.CANCELLED; throw t; } }
nextWaiter
模拟3个线程:线程1、线程2、线程3 抢占锁时,数据结构发生的变化
reportInterruptAfterWait(interruptMode);
头节点为 0,说明后续已被唤醒
isOnSyncQueue()
持有锁的线程是否为当前线程?执行重入判断
货物Object[] goods
参考:https://juejin.cn/post/6844903600414539789
这里与AQS获取锁代码一致
创建当前线程node节点队列tail是否为null?是:初始化哑元节点font color=\"#ff0000\
return true成功释放锁结束
ReenTranctLock可重入锁,释放
状态4:线程3 tryAcquire获取失败,同里得:
ReentrantLock.unLock(){ sync.release(1);}
4.1 唤醒消费队列中一个等待者线程
获取失败应该搁置改变前驱节点状态 :-1
OwnerThread步骤1:线程2步骤3:线程1
//设置占用线程为当前线程setExclusiveOwnerThread(current)
没有这步
tryAcquire(arg)再次尝试获取锁
模拟:3线程工作1.线程1 获得锁然后 await:进入Condition队列2.线程2 获得锁然后 await:进入Condition队列3.线程3 获得锁然后 signal,将Condition队列中线程1,转移到AQS队列;然后释放锁4.线程1 在await中被唤醒,通过tryAcquire()方法抢占到锁5.线程1 运行代码后 signal ,将Condition队列中线程2,转移到AQS队列;然后释放锁6.线程2 在await中被唤醒,通过tryAcquire()方法抢占到锁
AQS队列
tryAcquire(arg)加锁
FairSync
状态1:线程1 通过tryAcquire ,成功抢先获取锁
tryRelease(arg)解锁
condition = lock.newCondition()condition.await()
1.线程在这里被阻塞,等待unpark2.interrupted 标记当前线程中断过
可重入锁--,未释放锁
4.0 发现库存未满,进行生产,生产完成后,唤醒消费者emptyAwait.signal()4.3释放锁
梦醒来的地方-.-
唤醒别的线程
获取锁后,进入等待
加锁
c==0
公平锁实现
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //判断队列中是否有线程等待 if (font color=\"#ff0000\
清理取消了的线程节点
收藏
0 条评论
下一页