1,AbstractQueuedSynchronizer同步框架
1)AQS基本结构
image.png
构建锁和同步组件的基础,底层乐观锁,大量使用CAS操作+自旋for(;;)重试,轻量、高效地获取锁。
volatile int state: 表示同步状态;FIFO队列: 用于存储挂起的线程节点。
独占锁的exclusiveOwnerThread,表示当前持有锁的线程。
2)队列Wait queue node(包装当前线程)
1.前驱节点释放锁时,当前node被signaled通知。
2.prev指针,主要是用于处理node的cannelled状态,和处理addWaiter操作非原子。
3.next指针,主要用于singnal next node。
4.nextWaiter指针,用于区分共享/独占节点以及组成Condition单向队列。
image.png
队列结构图
image.png
3)CAS操作
sun.misc.Unsafe类的objectFieldOffset(Field field)获取某个字段相对Java对象的“起始地址”的偏移量。
image.png
Unsafe进行CAS。传入this、字段的offset偏移量、旧值、新值。
image.png
4)AQS模板方法
acquire + tryAcquire(int) :独占式获取锁,state 0->1。返回bolean值。
release + tryRelease(int) :独占式释放锁,state 1->0。返回bolean值。
acquireShared + tryAcquireShared(int) :共享式获取锁。返回int,>=0表示成功。
releaseShared + tryReleaseShared(int) :共享式释放锁。返回boolean值。
2,AQS独占锁节点waitStatus变迁
独占模式通常只关心0(初始化)、-1(signal)、1(cancelled)
image.png
1)初始化状态0。
对于正常的sync nodes,waitStatus初始化为0
addWaiter方法中,新增节点new Node(Thread.currentThread(), mode);
FIFO队列为空时,使用new Node()的dummy节点初始化head头
enq的两种情况:1.队列为空,初始化dummy节点(然后自旋)。2.tail变动,CAS执行失败。
image.png
2)根据node和pred判断node是否park以及修改pred的waitStatus。
1. 新节点入队,会把pred前驱节点状态修改为SIGNAL(-1),代表后继节点需要解除阻塞。
image.png
2.步骤。pred初始化为 0 -> CAS修改为pred为 -1(signal),第一次不阻塞而是再循环一次 -> 线程自旋发现pred为-1,park当前线程。
signal:后继给pred设置,相当于一个闹钟,适当的时候通知自己。
image.png
3)独占锁释放
image.png
此时head的successor线程继续自旋,tryAcquire成功。把head的successor修改为head,setHead(node)。
image.png
3,addWaiter尾分叉与unparkSuccessor从后往前遍历
1)尾分叉
image.pngenq方法非原子
2)unparkSuccessor方法
head的后继为null、或者状态为calcelled
image.png
4,AQS共享锁
1)acquireShared(
image.pngAQS实现
)和tryAcquireShared(目标方法子类实现
)方法
2)可以被多个线程持有。获取锁和释放锁,都可以唤醒后继节点。
nextWaiter属性赋值:独占锁addWaiter(Node.EXCLUSIVE)
,共享锁addWaiter(Node.SHARED)
获取锁成功,可以doReleaseShared
唤醒后继节点
image.png
3)doAcquireShared方法
image.png
4)doReleaseShared方法
(重点:h != head时,会再次进入循环)多个线程同时调用doReleaseShared会形成调用风暴,极大加速了后继节点的唤醒速度。
image.png
网友评论