AQS思路实现细节理解

 

成员变量 

1.state 用来判断资源是否被释放的标记位     volatile保证了线程之间的可见性

思考为什么不使用boolean 更小的空间 而且有二义性   ->  独占和共享  state需要表示 共享线程的数量

2.头结点 尾节点     FIFO 双向链表

 

Node 内部类

还有一个ConditionObject内部类

 

我们主要关注如何State 和先进先出 的队列来管理多线程的同步状态

考虑两种业务场景

1.获取锁 获取不到返回false

2.获取锁 愿意等待

 

 

 AQS 给上层调用开放空间  上层必须Override这个方法 来自由编写业务逻辑   比如说Reentrantlock的sync的实现

使用到了 ** 的设计模式 

tryAcquire 非公平    

公平的实现

 

 

 

 如果是选择等待获取锁的场景 可以直接使用AQS 中等待锁的acquire实现 而不用自己去实现复杂的排队处理

 

 

 

 继承类不可重写 

tryAcquire失败 调用 addWaiter 

将当前线程封装成node  加入等待队列  返回当前节点

 

 

 尾节点为空或者cas失败 进入完整的入队方法   对当前队列进行初始化  并且自旋的方式将当前节点插入  直到入队成功

疑惑: 为什么不直接使用完整的入队 方法  而是 先尝试入队?  完全入队多了一个判空操作    可能是重复的判空操作会影响性能

加入队列后 还要尝试让队列动起来  

一开始我陷入为主的认为 遵循了生产消费模型  让消费者去获取节点 然后让节点中封装的线程去拿锁

看看作者的想法:

acquireQueued 方法  和release方法 对线程进行挂起和响应  以此实现先入先出

    @ReservedStackAccess
    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; // help GC failed = false; return interrupted; }
          //判断当前节点是否应该挂起 其实不挂起一直自旋也行 但是如果大量线程自旋 会导致性能问题 所以应该把没有轮到它出队的线程挂起合适的时间唤醒
          // 等待状态的线程被中断会抛出中断异常 运行状态会改变一个中断状态值不影响运行 interrupt isinterrupted interrupted if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())   //第二个与条件 执行真正的挂起操作 LockSupport.park() interrupted = true; } } finally {
       //return 之前failed才可能是false 抛出异常后 cancleAcquire waitState 设置成CANCLE 以及一些清理操作 if (failed) cancelAcquire(node); } }

  

 

 

 

 

 

 什么时候挂起?

当前的节点之前除了head还有其他节点  并且前一个 节点也是siginal  当前挂起  保证只有一个线程在自旋

 

线程在什么时候被唤醒?  应该是一个线程 释放锁的时候

 

 

 

 

 

 

 

传进来的head 该方法是为了唤醒Head 后面的node 使得其自旋地获取锁  head的status应该设置为0这个默认值

    private void unparkSuccessor(Node node) {

        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);


        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

 

从尾结点开始搜索  找到除head外最靠前的 并且status<=0的节点  对其进行unpark

被唤醒就会继续执行acquireQueue方法  尝试获取锁

为什么从后往前找?

Thread的interrupt操作 ?

 

 

 

 
posted @ 2022-03-27 23:32  贪、欢  阅读(84)  评论(0)    收藏  举报