AQS框架

作者: 沐兮_d64c | 来源:发表于2019-04-18 22:57 被阅读0次

    1,AbstractQueuedSynchronizer同步框架

    1)AQS基本结构
    构建锁和同步组件的基础,底层乐观锁,大量使用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
    队列结构图
    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)
    1)初始化状态0。
    对于正常的sync nodes,waitStatus初始化为0
    addWaiter方法中,新增节点new Node(Thread.currentThread(), mode);

    image.png
    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)尾分叉enq方法非原子

    image.png
    2)unparkSuccessor方法
    head的后继为null、或者状态为calcelled
    image.png

    4,AQS共享锁

    1)acquireShared(AQS实现)和tryAcquireShared(目标方法子类实现)方法

    image.png
    2)可以被多个线程持有。获取锁和释放锁,都可以唤醒后继节点。
    nextWaiter属性赋值:独占锁addWaiter(Node.EXCLUSIVE),共享锁addWaiter(Node.SHARED)
    获取锁成功,可以doReleaseShared唤醒后继节点
    image.png
    3)doAcquireShared方法
    image.png
    4)doReleaseShared方法
    (重点:h != head时,会再次进入循环)多个线程同时调用doReleaseShared会形成调用风暴,极大加速了后继节点的唤醒速度。
    image.png

    相关文章

      网友评论

          本文标题:AQS框架

          本文链接:https://www.haomeiwen.com/subject/glmtcqtx.html