AQS源码解析(ReentrantLock)
创始人
2025-01-08 20:36:04
0

        什么是AQS:Juc中的大多数同步器都是围绕着一些相同的基础行为,比如等待队列,条件队列,共享,独占获取变量这些行为,抽象出来就是基于AQS(AbstractQueuedSynchronizer)实现的。所以可以把AQS看成这些行为的一个整合。

        管程:管程是指管理共享变量,以及对共享变量操作的过程,以此来让它们支持并发。不管是sychronized还是ReentrantLock,都是基于MESA的管程模型实现的

入口等待队列:

        如果共享资源已经被占用,那么其余线程会被阻塞在里面,等待唤醒的时候才会再去竞争共享变量

条件变量等待队列:

        ReentrantLock中的Condition.await()的时候会进入对应的条件队列,调用signal()的时候会从条件队列进入到入口等待队列,sychronized中只有一个条件等待队列(因为没有Condition),这个队列实现了阻塞唤醒的功能。

      来看一下ReentrantLock.lock()中的关键代码

//尝试获取共享变量  protected final boolean tryAcquire(int acquires) {         final Thread current = Thread.currentThread();         //获取共享变量         int c = getState();         if (c == 0) {             //cas尝试修改共享变量,如果成功了,那么将独占线程设置成当前线程             if (compareAndSetState(0, acquires)) {                 setExclusiveOwnerThread(current);                 return true;             }         }         else if (current == getExclusiveOwnerThread()) {             //锁重入逻辑,state+1             setState(c + acquires);             return true;         }         return false; } 

           tryAcquire方法中做的事情就是尝试去获取共享变量state(默认是0),如果获取到了那么通过cas将其修改成1,然后将其独占线程设置成当前线程,如果是锁重入,那么就将state再次+1,如果是多个线程进来,未获取到共享变量的线程,接下来会走到addWaiter中的enq()方法。

    //构造阻塞队列     private Node enq(final Node node) {         for (;;) {             //第一次进来的时候肯定是null             Node t = tail;             if (t == null) { // Must initialize                 //通过cas设置头尾指针,都指向空Node(首节点)                 if (compareAndSetHead(new Node()))                     tail = head;             } else {                 //第二次循环的时候,首结点已经构造完                 node.prev = t;                 //将尾指针指向该节点,并且绑定和首节点之间的指向关系                 if (compareAndSetTail(t, node)) {                     t.next = node;                     return t;                 }             }         }     }

          enq()主要用来创建阻塞队列,构造空节点为头结点,并且将头结点的next变量指向传入的节点,将传入节点的prev变量指向头结点。如果已经构造过了阻塞队列,那么会在addWaiter中就直接创建节点之间的指向关系,然后返回。并且会跳至acquireQueued方法

    final boolean acquireQueued(final Node node, int arg) {         boolean failed = true;         for (;;) {             //前置节点如果是头结点,那么会再次去竞争共享资源             final Node p = node.predecessor();             if (p == head && tryAcquire(arg)) {             //如果竞争成功,那么就会将头结点设置成该节点,并且将其线程和prev全部置空                 setHead(node);                 p.next = null; // help GC                 failed = false;                 return interrupted;             }             //如果获取不到,那么就调用lockSupport.park进行阻塞,等到释放锁的时候被唤醒             if (shouldParkAfterFailedAcquire(p, node) &&                 parkAndCheckInterrupt())                 interrupted = true;         }        }

        构造完阻塞队列入队之后,并不会立马就进行阻塞,而是会先判断他的前置节点是否是头结点,如果是,那么会再一次进行资源竞争(所以如果是公平锁,被唤醒之后会将前置节点是头结点的那个节点出队然后获取资源)。如果获取不到那么就调用lockSupport.park()进行阻塞。

        公平锁和非公平锁的区别:公平锁只会从阻塞队列里面按照入队顺序一个个的去竞争,非公平锁在释放锁的时候,如果有线程进来了,那么也会去立马抢占锁,并不会按顺序入到阻塞队列中,这是两者的最大区别。

        条件队列的源码可以自行再去解读一下(主要就是调用了await和signal方法,await的时候入条件队列,signal的时候从条件队列转到阻塞队列),本文主要讲解了获取锁的整个流程。但是现在一般用的都是微服务架构,分布式锁的实现和源码解析可以参考下文 分布式锁源码解析

上一篇:自制迷宫游戏 c++

下一篇:python基础语法

相关内容

热门资讯

攻略辅助!新九天作弊系统(辅助... 攻略辅助!新九天作弊系统(辅助)原来一直总是有辅助攻略(哔哩哔哩)进入游戏-大厅左侧-新手福利-激活...
黑科技辅助挂!超级三加一辅助工... 黑科技辅助挂!超级三加一辅助工具!总是是有开挂辅助教程(今日头条)-哔哩哔哩黑科技辅助挂!超级三加一...
绝活辅助!新蜜瓜大厅破解(辅助... 绝活辅助!新蜜瓜大厅破解(辅助)确实是有辅助软件(哔哩哔哩)暗藏猫腻,小编详细说明新蜜瓜大厅破解破解...
黑科技插件!丽水都莱辅助工具试... 您好,丽水都莱辅助工具试用这款游戏可以开挂的,确实是有挂的,需要了解加去威信【136704302】很...
据权威媒体报道!pokemmo... 据权威媒体报道!pokemmo辅助器手机版下载!确实确实有开挂辅助教程(详细教程)-哔哩哔哩1.po...
攻略辅助!逍遥辅助下载地址(辅... 攻略辅助!逍遥辅助下载地址(辅助)原来是真的有辅助方法(哔哩哔哩)1、下载好逍遥辅助下载地址正确养号...
2026版教程!海盗来了辅助哪... 2026版教程!海盗来了辅助哪个好!都是是有开挂辅助挂(有挂技术)-哔哩哔哩1、海盗来了辅助哪个好免...
攻略辅助!情怀游戏辅助器(辅助... 攻略辅助!情怀游戏辅助器(辅助)一贯真的有辅助插件(哔哩哔哩)1、实时情怀游戏辅助器透视辅助更新:用...
演示辅助!蘑菇云辅助(辅助)果... 演示辅助!蘑菇云辅助(辅助)果然存在有辅助攻略(哔哩哔哩)1、该软件可以轻松地帮助玩家将蘑菇云辅助辅...
今天下午!福建大玩家辅助器!总... 今天下午!福建大玩家辅助器!总是存在有开挂辅助神器(有挂秘籍)-哔哩哔哩1、今天下午!福建大玩家辅助...