AQS学习
尽可能结合源码来分析学习AQS
绅士jiejie
理想的生活,就是生活的理想!
展开
-
AQS-为什么说调用await()方法的线程一定要持有锁?
没持有锁的线程调用await()方法的测试代码:public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); try { condition.await(); } catch (InterruptedException e) { e.printStackTrace();原创 2020-07-07 22:36:37 · 1417 阅读 · 3 评论 -
AQS-为什么说调用signal()方法的线程一定要持有锁?
没持有锁的线程调用signal()方法的测试代码:public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); condition.signal();}测试结果:抛出了IllegalMonitorStateException异常。看下signal()方法的代码:可以发现isHeldExclusiv原创 2020-07-07 22:34:30 · 664 阅读 · 2 评论 -
AQS-等待队列中的节点是怎么加入到对同步状态获取的竞争中的
关键代码如下:第一个红框的代码会阻塞当前线程,第二个红框的代码会把节点加入到获取同步状态的竞争中。这里的isOnSyncQueue(node)方法主要的作用就是判断节点是否在同步队列中,如果不在,返回fasle,就会在while循环中调用LockSupport.park(this)阻塞住当前线程,那线程什么时候会被唤醒,当然是当有持有锁的线程调用了signal()方法且该节点处于等待队列的首节点时,就会被唤醒,关键代码如下:第一个红框代码作用是把节点加入到同步队列,第二个红框代码作用是把节点对应线原创 2020-07-07 22:32:59 · 921 阅读 · 0 评论 -
AQS-await()方法中的acquireQueued()方法为什么使用savedState做参数
acquireQueued(node, savedState)方法是用来把节点加入到对同步状态的竞争,node参数代表节点,savedState参数代表着要获取的同步状态数,那么问题来了,这里的同步状态数目为什么是savedState呢?其实答案很简单,savedState是fullyRelease(node)方法的返回值,该值其实就是当前线程锁持有的同步状态数,释放了多少的同步状态数,那么下次要去竞争同步状态时,就会去竞争和之前释放的同步状态数一样的数目。对于释放出同步状态的线程来说,失去的同步状态,.原创 2020-07-07 22:31:41 · 562 阅读 · 0 评论 -
AQS-isOnSyncQueue()方法解析
首先要知道isOnSyncQueue(node)方法主要的作用就是判断节点是否在同步队列中。isOnSyncQueue()方法源码如下:final boolean isOnSyncQueue(Node node) { //判断节点的状态,如果状态是CONDITION,说明节点肯定不在同步队列中,同时哪怕同步队列是刚刚初始化的,也会有一个冗余的头节点存在,所以节点的前驱节点如果为null,那么节点也肯定不在同步队列中,返回fasle if (node.waitStatus == Node.C原创 2020-07-07 22:30:59 · 1281 阅读 · 0 评论 -
AQS-doSignalAll()方法解析
首先要了解doSignalAll()方法的作用是唤醒等待队列中的所有节点线程先看看调用了doSignalAll的方法signalAll(),会发现在signalAll()方法中会取得等待队列首节点,并把它作为参数传进doSignalAll()方法中...原创 2020-07-07 22:28:01 · 685 阅读 · 0 评论 -
AQS-了解下等待队列和同步队列的交互
首先看一下ConditionObject类的相关属性:可以发现ConditionObject类是Condition的子类,那么接着看看Condition:发现就是个接口,里面提供了一些方法,根据方法名可以猜测,大致是阻塞等待和唤醒的方法,暂不深入,留个印象。返回ConditionObject类看看它有什么切入口可以让我们去理解它的作用,看看它拥有的方法:发现还是看不出什么来,那么换一个切入点,找一个AQS子类相关的同步器来作为突破口,这里使用ReentrantLock,那么就先找找Reentr原创 2020-07-07 12:07:36 · 2532 阅读 · 0 评论 -
AQS-doAcquireNanos()方法中nanosTimeout 」 spinForTimeoutThreshold作用
doAcquireNanos()方法部分结构如下:上述代码起到的作用是,判断后继节点不是首节点或者获取同步状态失败的线程节点是否要阻塞,而阻塞该线程节点需要满足shouldParkAfterFailedAcquire()==true以及nanosTimeout > spinForTimeoutThreshold两个判断条件,shouldParkAfterFailedAcquire()方法主要的作用是设置节点的状态为-1以及消除那些已经被取消的节点线程,这里重点看下为什么还要有nanosTimeo原创 2020-07-06 12:31:48 · 865 阅读 · 0 评论 -
AQS-hasQueuedPredecessors()解析
hasQueuedPredecessors()方法源码如下:public final boolean hasQueuedPredecessors() { //读取头节点 Node t = tail; //读取尾节点 Node h = head; //s是首节点h的后继节点 Node s; return h != t && ((s = h.next) == null || s.thread != Thread.curre原创 2020-07-06 12:31:03 · 6643 阅读 · 27 评论 -
AQS-hasQueuedPredecessors()为什么先读取tail,再读取head
hasQueuedPredecessors()源码如下:如上截图代码,因为tail以及head对象都是用volatile修饰符修饰的,所以可以防止指令重排序,保证一定是先读取tail,再读取head。那么为什么一定先读取tail,再读取head呢,涉及到多线程并发操作,可能会出现如下几种情况:有一个队列初始化的方法如下,该方法会对tail和head对象做一定的修改:其中compareAndSetHead(new Node())方法与tail = head赋值操作二者并不具备原子性。假设有两个原创 2020-07-06 12:23:55 · 658 阅读 · 3 评论 -
AQS的核心方法-release()解析
release()方法源码如下:public final boolean release(int arg) { //tryRelease()方法由子类实现,由子类来决定是否能成功释放锁 if (tryRelease(arg)) { //取得队列头节点 Node h = head; //如果头节点不为null而且头节点的状态不为0,0代表的是新建节点状态 if (h != null原创 2020-07-06 00:32:22 · 1125 阅读 · 0 评论 -
AQS-节点的起始状态0是怎么来的,为什么要有0这个状态?
首先看下代表节点状态的是哪个字段:这时候可以发现waitStatus是int类型,默认值自然就为0了。所以这时候就可以先知道,为什么节点的起始状态是0。那么接下来看一段代码:从以上截图可以发现,当ws的值是0时,会走进else中的逻辑,通过CAS把0设置为-1,那为什么不一开始就设置为-1呢,还要多出这一步?这里分享一个简单的理解思路,状态存在即是合理,会不会有一些方法,是在节点状态值为0时需要调用的?而正是因为有一些方法是需要在状态值为0时被调用,所以才会保留有0这个状态。...原创 2020-07-05 18:37:01 · 1430 阅读 · 3 评论 -
AQS-为什么队列头节点的Thread是null
如果是队列刚刚初始化的头节点,是这样的:可以发现这Node类的构造函数没做任何赋值,而Node类中有一个属性是Thread那既然没赋值,thread对象就肯定是null了。所以队列初始化的头节点的Thread是null。如果队列已经初始化完成,头节点设置如下:可以发现设置新的头节点时,新的头节点的thread也会被设置为null。那为什么队列头节点的Thread要是null呢?这里可以这样去理解,头节点是不参与排队的,因为它已经获得了同步状态了,那么就说明该头节点的相关线程.原创 2020-07-05 17:47:44 · 3643 阅读 · 7 评论 -
AQS-新加入的节点是怎么进入队列的
首先节点会进入队列,肯定是竞争同步状态失败了,所以会调用addWaiter()方法来入队,addWaiter()方法如下:如截图中所示,在这个方法中,会构建出一个与当前线程相关的新节点,然后判断尾节点是否不为null,如果不为null,则说明队列已经初始化成功了,那么就把新的节点设置为尾节点,如果尾节点为空,那么说明队列还没完成初始化,那么就走enq()方法,enq()方法如下:如上截图,enq()方法有一个无限循汗逻辑,既然走到了这步,就说明队列还没完成初始化,假设没有其他的线程捷足先登做了初始化原创 2020-07-05 17:29:46 · 1055 阅读 · 0 评论 -
AQS-了解节点自旋获取同步状态
原创 2020-07-04 18:00:14 · 3259 阅读 · 0 评论 -
AQS-为什么只有前驱节点是头节点才能尝试获取同步状态
为什么会有这疑问,关键代码如下:可以发现,p就是当前node节点对象的前驱节点,而只有当p是头节点时,判断才不会短路,才能去尝试获取同步状态。否则的话,就会走shouldParkAfterFailedAcquire()方法:通过shouldParkAfterFailedAcquire()方法,会让前驱节点不是头节点的节点进入等待。这时就会有为什么只有前驱节点是头节点才能尝试获取同步状态的疑问了?因为只有头节点才是成功获取了同步状态的节点,而当头节点释放了同步状态后,头节点会唤醒它的后继节原创 2020-07-04 17:27:15 · 1528 阅读 · 3 评论 -
AQS的核心方法-acquire()解析
以下是AQS的acquire()方法源码:public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }一步步拆解,先看下tryAcquire()方法,这个方法是由AQS子类来做具体实现的,暂不关注,重点关注AQS的几个模版方法原创 2020-07-04 17:01:23 · 5569 阅读 · 1 评论 -
AQS同步队列中的节点状态
状态说明SIGNAL值为-1,后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,那么就会通知后继节点,让后继节点的线程能够运行CONDITION值为-2,节点在等待队列中,节点线程等待在Condition上,不过当其他的线程对Condition调用了signal()方法后,该节点就会从等待队列转移到同步队列中,然后开始尝试对同步状态的获取PROPAGATE值为-3,表示下一次的共享式同步状态获取将会无条件的被传播下去CANCELLED值为...原创 2020-07-04 10:39:45 · 6669 阅读 · 0 评论 -
自定义一个独占锁
独占锁demo,基本模仿ReentrantLock:package com.example.springboot.codedemo;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.AbstractQueuedSynchronizer;import java.util.concurrent.locks.Condition;/** * 自定义一个独占锁,这里用公平锁 */public class M原创 2020-07-04 10:24:45 · 245 阅读 · 0 评论 -
AQS哪些方法是可以重写,哪些方法是关键模版方法?
同步器可以重写的方法方法作用protected boolean tryAcquire(int arg)这是独占式获取同步状态的方法,该方法的实现需要查询到当前的同步状态,同时做出相应判断,最后再通过CAS设置同步状态protected boolean tryRelease(int arg)这是独占式释放同步状态的方法,让那些等待获取同步状态的线程能够有机会获取同步状态protected int tryAcquireShared(int arg)这是共享式获取同步状.原创 2020-07-03 22:47:39 · 3727 阅读 · 0 评论 -
同步器使用了哪种设计模式?
这里看下AQS中的acquire方法:先不管这个方法是做什么的,但是是不是发现了一个熟悉的方法名—tryAcquire,它在AQS类中是个抽象方法,如下:这就说明了,它具体的实现是在子类里,有如下这么多的子类对它做了重写:但是其他方法的逻辑,比如addWaiter,acquireQueued以及selfInterrupt,AQS都自己做了具体的实现,同时要么使用了final关键字防止方法被重写,要么使用private修饰符表示方法是私有的,或者要么干脆就是静态方法总结对以上的做法做原创 2020-07-03 22:29:39 · 371 阅读 · 0 评论 -
AQS是怎么使用的
这里先以ReentrantLock类为切入口,看下它是怎么使用AQS从上图可以发现,ReentrantLock类中有一个Sync类,而Sync的父类是AbstractQueuedSynchronizer,所以这里可以确定的是ReentrantLock类是通过自己的一个内部类来和AQS产生联系的。再看看ReentrantLock类的构造方法:通过以上这两个方法可以决定锁是否是公平的,然后继续会发现原来还有两个类,一个是NonfairSync,另一个是FairSync。那就看看NonfairSy.原创 2020-07-03 22:27:18 · 833 阅读 · 0 评论 -
AQS是什么
AQS是什么AQS类名全称AbstractQueuedSynchronizer,我们也可以把它称之为队列同步器,AQS是Concurrent包的核心,Concurrent包提供的很多工具类,底层就是依赖AQS来做实现的,比如我们熟悉的ReentrantLock,Semaphore,CountDownLatch等。...原创 2020-07-03 22:22:48 · 362 阅读 · 0 评论