接下来总结一下AQS框架,看来很多博文其中有很多非常有用的信息,通过这些博文再加上自己对源码的总结完成了以下这篇博文,文笔和技术有限,只能当做参考总结,如果有错误或者不明白的地方可以留下评论我们共同学习共同进步。
学习AQS框架之前以下几个基础知识概念得必须明白理解,不然你学习起来可能会很费劲。
什么是CAS 或者说什么是无锁机制的CAS?
平时我们使用的Synchronized、ReentrantLock这些都是属于独占锁也可以叫做悲观锁,独占锁意味着一次只能有一个线程获取到锁其他线程必须得等待前面获得锁的线程执行完毕。
无锁机制就是一种乐观的策略,也就是说假设对共享资源之间没有冲突,线程可以不停的执行,如果发生冲突我们则可以使用CAS技术来保证线程安全。
什么又是CAS呢?CAS就是一种很高级的无锁策略。CAS(Compare And Swap)即比较交换,其核心算法就是给定更新的变量判断值是否和预期值相等,如果是则将传入的值进行修改,否则就是有线程在之前将我们需要更新的变量给修改过了,则不进行操作,但我们可以选择是否继续尝试或者放弃操作。继续操作有个很关键的概率就是自旋锁,一种充分利用CPU的方式来获取更新。
什么又是自旋锁获取呢?在CAS中如果有其他线程将我们传入的值进行修改过了,我们则读取失败此时就能够使用自旋的操作,即一个while(true)的死循环,直到CAS能够拿到最新的值为之并能够成功更新。下面展示一段非常经典的CAS自旋volatile变量更新值的代码,该代码来源于AtomicInteger的getAndIncrement()方法的源码
上面介绍了CAS无锁机制和自旋锁的概念,接下来我们可以介绍AQS框架啦,先看一张概念图。
AQS框架内部维护了一个Node双向链表的内部类,上图中的head方法我们可以看做正在执行的线程,为什么阻塞队列没有包括head呢,因为head是正在执行的线程,阻塞队列中的线程会依次等待head唤醒其后一个node线程,并将下一个节点设置成头节点执行。接下来我们看看AQS中Node节点的内部类的变量
//标识节点是共享节点
static final Node SHARED = new Node();
//标识节点是独占节点
static final Node EXCLUSIVE = null;
//线程取消竞争锁
static final int CANCELLED = 1;
//用来判断唤醒下一个线程
static final int SIGNAL = -1;
//等待条件
static final int CONDITION = -2;
static final int PROPAGATE = -3;
//节点状态 我们从上面观察我们可知如果waitStatus>0则说明节点取消竞争了,
// 如果waitStatus<0代表节点是有效状态 waitSatus=0说明是初始化的状态
volatile int waitStatus;
//下面两个节点能够用来构建双向链表
//节点的上一个节点
volatile Node prev;
//节点下一个节点
volatile Node next;
//排队此节点的线程。在构造上初始化并在使用后消失。
volatile Thread thread;
可能没有完全掌握的朋友又要问什么是volatile了,volatile只要记住该变量的修改能够被其他线程所见,如果你不声明 如果我修改int 1 为2此时如果其他线程同时进来则会造成读取时会脏读,会读取到没有更新的int值为1。
接下来介绍一下AQS类的核心变量
// 头结点,你直接把它当做 当前持有锁的线程 可能是最好理解的
private transient volatile Node head;
// 阻塞的尾节点,每个新的节点进来,都插入到最后,也就形成了一个链表
private transient volatile Node tail;
// 这个是最重要的,不过也是最简单的,代表当前锁的状态,0代表没有被占用,大于0代表有线程持有当前锁
// 之所以说大于0,而不是等于1,是因为锁可以重入嘛,每次重入都加上1
private volatil