回顾前面:
只有光头才能变强!
本来我是打算在这章节中写Lock的子类实现的,但看到了AQS的这么一个概念, 可以说Lock的子类实现都是基于AQS的 。
AQS我在面试题中也见过他的身影,但一直不知道是什么东西。所以本篇我就来讲讲AQS这个玩意吧,至少知道它的概念是什么,对吧~
那么接下来我们就开始吧~
首先我们来普及一下juc是什么: juc其实就是包的缩写(java.util.concurrnt)
我们可以发现lock包下有三个抽象的类:
通常地: AbstractQueuedSynchronizer简称为AQS
我们Lock之类的两个常见的锁都是基于它来实现的:
那么我们来看看AbstractQueuedSynchronizer到底是什么,看一个类是干什么的 最快途径就是看它的顶部注释
通读了一遍,可以总结出以下 比较关键 的信息:
上面也提到了AQS里边最重要的是状态和队列,我们接下来就看看其源码是怎么样的...
使用volatile修饰实现线程可见性:
修改state状态值时使用CAS算法来实现:
这个队列被称为:CLH队列(三个名字组成),是一个 双向队列
看看它队列源码的组成:
static final class Node { // 共享 static final Node SHARED = new Node(); // 独占 static final Node EXCLUSIVE = null; // 线程被取消了 static final int CANCELLED = 1; // 后继线程需要唤醒 static final int SIGNAL = -1; // 等待condition唤醒 static final int CONDITION = -2; // 共享式同步状态获取将会无条件地传播下去(没看懂) static final int PROPAGATE = -3; // 初始为0,状态是上面的几种 volatile int waitStatus; // 前置节点 volatile Node prev; // 后继节点 volatile Node next; volatile Thread thread; Node nextWaiter; final boolean isShared() { return nextWaiter == SHARED; } final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { // Used to establish initial head or SHARED marker } Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } }
获取独占锁的过程就是在acquire定义的,该方法用到了 模板设计模式 ,由子类实现的~
过程:acquire(int)尝试获取资源,如果获取失败,将线程插入等待队列。插入等待队列后,acquire(int)并没有放弃获取资源,而是根据前置节点状态状态判断是否应该继续获取资源,如果前置节点是头结点,继续尝试获取资源,如果前置节点是SIGNAL状态,就中断当前线程,否则继续尝试获取资源。直到当前线程被park()或者获取到资源,acquire(int)结束。
来源:
释放独占锁的过程就是在acquire定义的,该方法也用到了 模板设计模式 ,由子类实现的~
过程:首先调用子类的tryRelease()方法释放锁,然后唤醒后继节点,在唤醒的过程中,需要判断后继节点是否满足情况,如果后继节点不为且不是作废状态,则唤醒这个后继节点,否则从tail节点向前寻找合适的节点,如果找到,则唤醒.
来源:
总结一下AQS到底是什么吧:
有兴趣的同学可去看源码和下面的链接继续学习,我这里就不讲述了。简简单单把AQS过一遍~
明天就看Lock显式锁实现咯~~~
参考资料:
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以 关注微信公众号:Java3y 。为了大家方便,刚新建了一下 qq群:742919422 ,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友