对AQS的源码解析理解

对AQS的源码解析理解
我们使用常用的ReentrantLock来解析AQS是怎么工作的,仅仅是自己的一些理解,希望大家指正。
首先我们进入lock()
对AQS的源码解析理解
我们发现,源码中是使用了一个sync.lock()来调用的,那么sync是什么呢?
对AQS的源码解析理解
我们跟踪源码发现sync是ReentrantLock中的一个属性,而Sync类就是继承了我们说的AQS,这也就说明了ReentrantLock其实就是使用AQS来做到一个对于线程的阻塞和通知唤醒的管理的。我们接着向下看。
对AQS的源码解析理解
我们发现sync.lock()进来之后是一个抽象方法,这其实是一种模板模式的应用了,AQS来定义出这些方法,但是具体的实现需要落实到具体的子类上。
对AQS的源码解析理解
我们这次追踪的是非公平锁,所以我们进入第二个
对AQS的源码解析理解
我们发现代码很简单,首先是使用了一个cas的方法尝试来将state变为1,这个state是什么呢,state==0,就说明当前锁无人持有,为1就说明有线程持有锁了,若是>=2,代表的是一个重入次数
。若是cas成功,则说明当前线程是成功获取锁了,就通过setExclusiveOwnerThread()将主线程设为自己,若是cas失败,则会执行acquire方法,来获得锁。
对AQS的源码解析理解
acquire里面是使用tryAcquire尝试获取锁,当获取失败的话,就会走后面的acquireQueued,这个就是将这个线程放入到等待队列了,我们先来看tryAcquire方法
对AQS的源码解析理解
又是模板设计模式的应用,我们仍然是进入非公平锁
对AQS的源码解析理解
对AQS的源码解析理解
就是线程会获取当前的state,然后若是c == 0,那么说明当前锁无人持有,下面又是一个cas,若是锁被人持有,那么会判断一下当前持有锁的线程是不是自己,若是自己的话,是可以重入的,这也是ReentrantLock的一个特性,否则返回false
对AQS的源码解析理解
当tryAcquire为false,那么加上一个!,就可以往下走,我们再看一个addWaiter,这个其实就是将线程放入到一个等待队列了,
对AQS的源码解析理解
首先会创建一个节点,这个node节点中是包含了当前线程的,这也是实现队列的一个机制,下面先来判断了一个tail节点是否为null,也就是看当前等待队列中是否有节点,若是没有的话会执行enq(),系统会自己创建一个没有内容的哨兵节点,作为head和tail(稍后再看源码),然后才会将当前线程节点放入到队列中,也就是说,队列中的第一个线程是哨兵线程,第一个线程入队之后其实已经是两个线程节点了。
对AQS的源码解析理解
tail为null的时候会将head设置为一个新的空节点,然后tail = head;否则就是将当前节点设为tail节点。
进入等待队列的一个大致流程大概是那么多,但是我们发现此时线程还没有阻塞啊?那么线程是在哪里阻塞的呢?
对AQS的源码解析理解
我们往前看的话发现acquireQueued还没有说,我们进去看一下
对AQS的源码解析理解
核心的代码就是循环中的,首先是若是自己是队列头部的节点,会再去尝试获取锁,失败的话就走下面的一个if,里面的两个方法
对AQS的源码解析理解
这个就是来改变node节点中的waitstatus,这个值默认是0,第一次会将其改为-1,表示后续线程需要取消连接,也就是当前已经阻塞了,之后会走parkAndCheckInterrupt方法(这个才是重点)
*对AQS的源码解析理解
代码很简单,但是需要一个LockSupport的前置知识, LockSupport就是可以提供一个类似wait和notify的作用(具体深入的理解需要下去看)看到这我们也就明白了,这个线程算是进入等待队列了,而且阻塞了。
下面我么看一下unlock();
对AQS的源码解析理解
一样的是一个sync调用的release方法
对AQS的源码解析理解
就是去尝试释放锁,释放成功的话就会调用unparkSuccessor方法对队列头部的线程发放一个许可证,也就是理解为唤醒线程
对AQS的源码解析理解
tryRelease()中还是比较简单的,就是就将state-1,之后看state是否等于0,等于0的话就返回true
对AQS的源码解析理解
大概是那么多,是一些自己的理解,希望大家指正

上一篇:简单的队列


下一篇:【学习笔记】队列、单调队列、循环队列,你了解多少?(C语言实现)