回顾前面:java
只有光头才能变强!面试
原本我是打算在这章节中写Lock的子类实现的,但看到了AQS的这么一个概念,能够说Lock的子类实现都是基于AQS的。算法
AQS我在面试题中也见过他的身影,但一直不知道是什么东西。因此本篇我就来说讲AQS这个玩意吧,至少知道它的概念是什么,对吧~设计模式
那么接下来咱们就开始吧~微信
首先咱们来普及一下juc是什么:juc其实就是包的缩写(java.util.concurrnt)多线程
咱们能够发现lock包下有三个抽象的类:框架
一般地:AbstractQueuedSynchronizer简称为AQSpost
咱们Lock之类的两个常见的锁都是基于它来实现的:学习
那么咱们来看看AbstractQueuedSynchronizer究竟是什么,看一个类是干什么的最快途径就是看它的顶部注释ui
通读了一遍,能够总结出如下比较关键的信息:
上面也提到了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。
文章的目录导航: