父类AbstractOwnableSynchronizer源码解析
AbstractOwnableSynchronizer为创建可能需要所有权概念的锁和同步器提供了基础
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
/** Use serial ID even though all fields transient. */
private static final long serialVersionUID = 3737899427754241961L;
/构造器
protected AbstractOwnableSynchronizer() { }
/独占模式同步器的当前所有者(一个线程对象)
private transient Thread exclusiveOwnerThread;
/设置独占模式同步器的所有者
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
/返回独占模式同步器的当前所有者
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
AQS内部类Node源码解析
/**
*等待队列的节点类
*通过Node可以实现两个对列
*1.通过prev和next实现CLH对列(线程同步对列,双向对列)
*2.nextWaiter实现Condition条件上的等待线程对列(单向对列)
*/
static final class Node {
//标识节点当前在共享模式下
static final Node SHARED = new Node();
//标识节点当前在独占模式下
static final Node EXCLUSIVE = null;
/**下面几个int常量是给waitStatus用*/
//此线程已经取消
static final int CANCELLED = 1;
//其表示当前node的后继节点对应的线程需要被唤醒
static final int SIGNAL = -1;
//线程在等待condition条件
static final int CONDITION = -2;
//共享模式下node有可能处于这种状态,表示锁的下一次获取可以无条件传播
static final int PROPAGATE = -3;
/**
* 取值范围只可能是
* SIGNAL: 其表示当前node的后继节点对应的线程需要被唤醒
* CANCELLED: 此线程已经取消。可能是超时或者中断。
* CONDITION: 此node当前处于条件队列中
* PROPAGATE: 当前场景下后续的acquireShared能够得以执行
* 0: 对于正常的同步节点,该字段初始化为0.
*/
volatile int waitStatus;
/**
* 当前节点的前驱节点,用于检查waitStatus
* 如当前节点取消,那就需要前驱节点和后继节点来完成连接
*/
volatile Node prev;
/**
* 指向当前节点在释放时唤醒的后续节点
*/
volatile Node next;
/**
* 入队列时的当前节点
*/
volatile Thread thread;
/**
* 存储condition对列中的后继节点
*/
Node nextWaiter;
/**
* 如果节点在共享模式下等待,则返回true
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* 返回当前节点的前驱节点
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
/**
*无参构造器
*/
Node() {
}
//构造器,用于addWaiter()方法
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
//构造器,用于condition
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
AQS实例属性源码解析
/*****AbstractQueuedSynchronizer的实例属性*****/
/**
* 等待队列的头结点
*/
private transient volatile Node head;
/**
* 等待队列的尾结点
*/
private transient volatile Node tail;
/**
* 同步状态
*/
private volatile int state;
/**
* state的getter方法
*/
protected final int getState() {
return state;
}
/**
* state的setter方法
*/
protected final void setState(int newState) {
state = newState;
}
AQS独占锁加锁
独占锁至少有两个功能:
- 获取锁的功能。当多个线程一起获取锁的时候,只有一个线程能获取到锁,其他线程必须在当前位置阻塞等待。
- 释放锁的功能。获取锁的线程释放锁资源,而且还必须能唤醒正在等待锁资源的一个线程。
独占锁加锁
acquire()方法
/**
*以独占的方式获取锁资源,不响应中断
*/
public final void acquire(int arg) {
//1.调用tryAcquire()方法,返回true则结束,
//tryAcquire()方法一般在AQS子类中实现
//2.如果tryAcquire()方法返回false
//调用addWaiter()方法进入队列
//3.调用acquireQueued()方法等待队列中获取资源
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
方法流程如下:
- tryAcquire()方法:尝试以独占方式获取资源,在AQS中是空方法,子类要重写该方法。通常state代表资源。不用锁中释义不同。获取到资源后返回true;反之,返回false,转入下个流程。
- addWaiter()方法:将线程加入队列尾部,并标记为独占模式。
- acquireQueued()方法:让线程在等待队列中获取获取资源,直到获取到资源才返回;如果等待过程中被中断过,则返回true,否则返回false。
- 3中线程在等待中被中断过,它是不响应。只是获取资源后才进行自我中断selfInterrupt(),设置中断标识。
tryAcquire()方法
/**
*尝试获取锁,由子类实现
*/
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
tryAcquire在AQS中是空方法,子类要重写该方法,这里没有定义成abstract方法,原因在于独占锁重写tryAcquire和tryRelease,共享锁重写tryAcquireShared和tryReleaseShared,如果定义成abstract抽象方法,则子类要实现四个方法。
addWaiter()方法
/**
*加入独占线程节点到队列尾上
*/
private Node addWaiter(Node mode) {
//用当前线程创建一个Node节点
Node node = new Node(Thread.currentThread(), mode);
//先尝试快读加入队列,成功则返回新节点Node
//失败,则采用自旋加入节点直至入队成功返回该节点
Node pred = tail;
//队尾非空
if (pred != null) {
//node节点的prev引用指向队尾节点
node.prev = pred;
//如果CAS入队尾成功
if (compareAndSetTail(pred, node)) {
//原队尾节点的next引用指向node
pred.next = node;
//返回node
return node;
}
}
//如果队尾为空
//或者通过CAS进入队尾失败,存在竞争
//通过enq()方法自旋
enq(node);
//返回node
return node;
}
addWaiter()方法调用的enq()方法
/**
*自旋方式使node进入队尾
*/
private Node enq(final Node node) {
//自旋
for (;;) {
//队尾节点
Node t = tail;
//如果队列为空
if (t == null) { // Must initialize
//创建head节点
//CAS设置队列头节点
if (compareAndSetHead(new Node()))
//此时队列只有一个节点
//头节点等于尾节点
tail = head;
} else {
//设置node节点的prev引用指向t
node.prev = t;
//CAS设置新的队尾节点为node
if (compareAndSetTail(t, node)) {
//原队尾节点的next引用指向node
//node就是新的尾节点tail
t.next = node;
//自旋结束,返回原尾节点
return t;
}
}
}
}
acquireQueued()方法
/**
*节点加入队列后,尝试在队列中自旋获取资源
*/
final boolean acquireQueued(final Node node, int arg) {
//标记是否成功拿到资源
boolean failed = true;
try {
//标记是否被中断
boolean interrupted = false;
//自旋
for (;;) {
//node的前驱节点,会抛出NullPointerException异常
final Node p = node.predecessor();
//如果node的前驱节点是队列头节点head
//head节点释放资源后
//紧接着就应该是node尝试获取资源
//因此调用tryAcquire()尝试获取资源
if (p == head && tryAcquire(arg)) {
//如果获取资源成功
//设置node为新的头结点
setHead(node);
//设置原头节点的后继节点next为null
//帮助垃圾收集
p.next = null;
//标记已经获取到资源
failed = false;
//返回interrupted=false
return interrupted;
}
//如果node的前驱节点不是队列头节点head
//或者node节点尝试获取资源失败
//进入此分支
//1.检查并更新无法获取资源的节点的状态
//如果线程应该阻塞,则返回true
//2.阻塞线程,并检查中断状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//如果1、2条件都满足,需要设置中断标识为true
interrupted = true;
}
} finally {
//如果node前驱节点为空,抛出NullPointerException异常
//说明node为头节点,此时failed为true
if (failed)
//取消对资源的获取
cancelAcquire(node);
}
}
/**
*获取节点的前驱节点,会抛出NullPointerException异常
*/
final Node predecessor() throws NullPointerException {
//指向当前节点的前驱节点的引用
Node p = prev;
//如果前驱节点为空,抛出NullPointerException
if (p == null)
throw new NullPointerException();
else
return p;
}
shouldParkAfterFailedAcquire()方法
/**
*线程获取资源失败后,判断是否阻塞线程
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//获取前驱节点的状态
//根据前驱节点的状态判断是否将线程阻塞
int ws = pred.waitStatus;
//如果前驱节点已经被设置为SIGNAL
if (ws == Node.SIGNAL)
/*
* 前驱节点释放资源后,马上唤醒后继节点。返回true,表示阻塞线程
*/
return true;
//如果前驱节点状态大于0,即CANCELLED状态
if (ws > 0) {
/*
* 前驱节点的线程被撤销,跳过所有的被撤销的prev节点
* 将上一个状态小于0的节点设置为node的前驱节点
*/
do {
//如果waitStatus>0
//修改node.prev指向前驱节点的前驱节点
node.prev = pred = pred.prev;
//while循环,waitStatus<0跳出循环
} while (pred.waitStatus > 0);
//退出while循环后,当前pred就是node的前驱节点
//修改pred节点的后继节点为node
pred.next = node;
} else {
/*
* 如果上面条件都不满足,此时waitStatus一定是0或者PROPAGATE
* 通过CAS设置前驱节点的状态为SIGNAL
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
//返回false表示不阻塞线程
//回到acquireQueued()方法,返回false会进入下一次自旋
return false;
}
parkAndCheckInterrupt()方法
/**
*阻塞线程,判断线程是否中断
*/
private final boolean parkAndCheckInterrupt() {
//park()会让当前线程进入waiting状态
//在此状态下,有两种途径可以唤醒该线程
//1.unpark()
//2.interrupt()
LockSupport.park(this);
//返回线程是否被中断,会清除中断标识
return Thread.interrupted();
}
如果parkAndCheckInterrupt()方法返回true,则acquireQueued()方法需要设置interrupt为true。acquireQueued()方法返回interrupted的状态。回到acquire()方法,如果acquireQueued()方法返回true,将会执行selfInterruput()方法。
selfInterruput()方法
/**
*中断线程,并不对中断做出响应
*/
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
小结
总结acquire()方法的流程。
- 尝试获取资源,如果获取资源成功,tryAcquire()方法返回true,则acquire()方法执行结束。否则进入步骤2。
- 如果尝试获取资源失败,tryAcquire()方法返回false,则执行addWaiter()方法将线程以节点的方法添加到队列的末尾。
- addWaiter()方法也许会存在竞争,造成入队尾失败的情况。需要通过自旋的方式入队尾。
- 入队尾成功后,通过acquireQueued()方法尝试在队列中获取资源或者阻塞线程。
- park()方法阻塞线程后,等待前驱节点调用unpark()方法或者线程中断唤醒线程。
- 判断线程是否中断并维护线程中断标识。
独占锁解锁
release()方法
/**
*释放独占的自旋
*会唤醒等待队列里的其他线程来获取资源
*/
public final boolean release(int arg) {
//如果tryRelease()释放资源成功
if (this.tryRelease(arg)) {
//获取队列的头节点
AbstractQueuedSynchronizer.Node h = this.head;
//如果队列的头节点非空
//并且头节点的waitStatus不等于0
//则队列中可能存在待唤醒的节点
if (h != null && h.waitStatus != 0) {
//唤醒后继节点,让后继节点竞争资源
this.unparkSuccessor(h);
}
//释放独占的资源成功,返回true
return true;
} else {
//释放独占的资源失败,返回false
return false;
}
}
tryRelease()方法
/**
*释放资源,由子类实现完成
*/
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
unparkSuccessor()方法
/**
*唤醒后继节点
*/
private void unparkSuccessor(AbstractQueuedSynchronizer.Node node) {
//节点的状态,如果小于0则肯定不是CANCELLED状态
int ws = node.waitStatus;
if (ws < 0) {
//CAS将节点状态修改为0
node.compareAndSetWaitStatus(ws, 0);
}
//节点node的后继节点
AbstractQueuedSynchronizer.Node s = node.next;
//如果后继节点非空,且状态大于0,即CANCELLED
//说明后继节点的线程取消对资源的等待
if (s == null || s.waitStatus > 0) {
//将后继节点置为null
s = null;
//从队列尾节点开始向前遍历
//找到队列中node节点后第一个等待唤醒的节点
//如果遍历到的节点t非空且不等于当前节点node
//则校验节点p的状态
for(AbstractQueuedSynchronizer.Node p = this.tail; p != node && p != null; p = p.prev) {
//如果节点的状态小于等于0
if (p.waitStatus <= 0) {
//则将s指向p
s = p;
}
}
}
//如果节点s非空
if (s != null) {
//唤醒节点s对应的线程
LockSupport.unpark(s.thread);
}
}
unparkSuccessor()方法执行流程
- 将node节点的状态设置为0;
- 寻找下一个非取消状态的节点p;
- 如果节点s不为null,则调用LockSupport.unpark(s.Thread)方法唤醒s所在线程。
注:唤醒线程也是有顺序的,就是添加到CLH队列线程的顺序。
小结
总结release()方法的流程
- 调用tryRelease()方法释放当前持有的锁资源;
- 如果完全释放了锁资源,那么就调用unparkSuccessor()方法,去唤醒一个等待锁的线程。