AQS是JUC当中最核心的部分,大部分多线程讲解,都不会详细讲AQS,AQS的源代码,要看明白仍是有点困难的。可是一旦看明白了,结构仍是蛮清晰的。这里咱们把AQS拆开,分红几部分来说,就会很清楚了。首先先从内部类Nde讲起php
上源代码:java
static final class Node { static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2; static final Node SHARED = new Node(); static final Node EXCLUSIVE = null; 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; } }
这个NODE类是java.util.concurrent.locks.AbstractQueuedSynchronizer的内部类。主要实现了一个链表的数据结构中的节点。先不谈这个链表是作什么的,咱们先看看这个节点是怎么构造的。数据结构
首先,这个节点有四个状态 ,其中三在Node类中已经明肯定义,另一个状态为初始状态,值为0多线程
static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2;
1 :当前节点被取消或者中断
并发
-1:下一个节点须要被释放this
-2:当前节点正处在等待队列中spa
0 :不属于任何一种.net
其次,这个节点有两种模式,其中SHARED是共享模式,EXCLUSIVE是独占模式。
线程
static final Node SHARED = new Node(); static final Node EXCLUSIVE = null;
等一下再介绍这两个模式。先把数据结构看完。指针
而后是成员变量:
volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter;
volatile int waitStatus:当前节点的状态,一共四种
volatile Node prev:链表的前一个节点
volatile Node next:链表的后一个节点
volatile Thread thread:这个节点所处的线程
Node nextWaiter:这个节点等待的模式(共享模式和独占模式)
接下去的两个方法和三个构造方法很是简单,就不讲了。
问题1:这个链表是如何工做的?
用一张图表示队列的工做方式:
每一个节点都拥有一个指针,知道本身的前一个节点
每一个节点都有一个状态位,表示是否占用资源
每一个节点都要完成自旋判断上一个节点的状态(图中while循环),才能真正的执行本身的逻辑
当逻辑执行完,会修改本身的节点的状态,放开下个节点的自旋
新插入的节点,永远在队尾
上图已经很是清楚的描述了这个队列的工做方式。这就是CLH锁的原理。其实实现锁还有几种不一样的方式。(点击了解更多锁的实现原理和比较)
因为一个线程的状态不止两种,因此Node的状态实现,并无用True/False表示。而是用了waitStatus表示。另外为了操做的方便,Node并无使用单向链表,而是双向链表。这样操做起来会方便不少。
以下图:
每一个节点都拥有一个指针,知道本身的前一个节点和后一个节点
每一个节点都有一个状态位,表示是否占用资源
每一个节点都要完成自旋判断上一个节点的状态(图中while循环),才能真正的执行本身的逻辑
当逻辑执行完,会修改本身的节点的状态,放开下个节点的自旋
新插入的节点,永远在队尾。队尾节点标记新插入节点
被取消或者中断的线程节点,会从链表中断开,被删除
还记得在讲LockSupport(点击查看源码)中是如何挂起和恢复一个线程的吗?Thread对象就是作这个的。
到此为止,咱们的图与Node类的结构是否是已经很像了呢?
问题2:共享模式和独占模式
共享模式和独占模式是两种不一样的锁定资源的方式。
最经典的举例就是对共享文件的操做。当第一个,第二个用户都请求读取文件内容时,并不会阻塞用户的读取行为,而且能够并发的读取。
可是当第三个用户请求修改的时候,就会获取一个独占锁。一旦获取了独占锁,全部共享锁的获取或者独占锁的获取都会被阻塞,一直等
到独占锁被释放才会解除阻塞。
实际在JUC中读写锁也是一个很典型的共享与独占的例子。