栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

对AQS的源码解析理解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

对AQS的源码解析理解


我们使用常用的ReentrantLock来解析AQS是怎么工作的,仅仅是自己的一些理解,希望大家指正。
首先我们进入lock()

我们发现,源码中是使用了一个sync.lock()来调用的,那么sync是什么呢?

我们跟踪源码发现sync是ReentrantLock中的一个属性,而Sync类就是继承了我们说的AQS,这也就说明了ReentrantLock其实就是使用AQS来做到一个对于线程的阻塞和通知唤醒的管理的。我们接着向下看。

我们发现sync.lock()进来之后是一个抽象方法,这其实是一种模板模式的应用了,AQS来定义出这些方法,但是具体的实现需要落实到具体的子类上。

我们这次追踪的是非公平锁,所以我们进入第二个

我们发现代码很简单,首先是使用了一个cas的方法尝试来将state变为1,这个state是什么呢,state==0,就说明当前锁无人持有,为1就说明有线程持有锁了,若是>=2,代表的是一个重入次数
。若是cas成功,则说明当前线程是成功获取锁了,就通过setExclusiveOwnerThread()将主线程设为自己,若是cas失败,则会执行acquire方法,来获得锁。

acquire里面是使用tryAcquire尝试获取锁,当获取失败的话,就会走后面的acquireQueued,这个就是将这个线程放入到等待队列了,我们先来看tryAcquire方法

又是模板设计模式的应用,我们仍然是进入非公平锁


就是线程会获取当前的state,然后若是c == 0,那么说明当前锁无人持有,下面又是一个cas,若是锁被人持有,那么会判断一下当前持有锁的线程是不是自己,若是自己的话,是可以重入的,这也是ReentrantLock的一个特性,否则返回false

当tryAcquire为false,那么加上一个!,就可以往下走,我们再看一个addWaiter,这个其实就是将线程放入到一个等待队列了,

首先会创建一个节点,这个node节点中是包含了当前线程的,这也是实现队列的一个机制,下面先来判断了一个tail节点是否为null,也就是看当前等待队列中是否有节点,若是没有的话会执行enq(),系统会自己创建一个没有内容的哨兵节点,作为head和tail(稍后再看源码),然后才会将当前线程节点放入到队列中,也就是说,队列中的第一个线程是哨兵线程,第一个线程入队之后其实已经是两个线程节点了。

tail为null的时候会将head设置为一个新的空节点,然后tail = head;否则就是将当前节点设为tail节点。
进入等待队列的一个大致流程大概是那么多,但是我们发现此时线程还没有阻塞啊?那么线程是在哪里阻塞的呢?

我们往前看的话发现acquireQueued还没有说,我们进去看一下

核心的代码就是循环中的,首先是若是自己是队列头部的节点,会再去尝试获取锁,失败的话就走下面的一个if,里面的两个方法

这个就是来改变node节点中的waitstatus,这个值默认是0,第一次会将其改为-1,表示后续线程需要取消连接,也就是当前已经阻塞了,之后会走parkAndCheckInterrupt方法(这个才是重点)
*
代码很简单,但是需要一个LockSupport的前置知识, LockSupport就是可以提供一个类似wait和notify的作用(具体深入的理解需要下去看)看到这我们也就明白了,这个线程算是进入等待队列了,而且阻塞了。
下面我么看一下unlock();

一样的是一个sync调用的release方法

就是去尝试释放锁,释放成功的话就会调用unparkSuccessor方法对队列头部的线程发放一个许可证,也就是理解为唤醒线程

tryRelease()中还是比较简单的,就是就将state-1,之后看state是否等于0,等于0的话就返回true

大概是那么多,是一些自己的理解,希望大家指正

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/292933.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号