AbstractOwnableSynchronizer
AbstractOwnableSynchronizer是一个线程持有者同步器,它定义了一个transient变量Thread exclusiveOwnerThread来表示一个排他的线程,仅提供了设置和获取该变量的方法。通过设置exclusiveOwnerThread来表示同步器被一个排他的线程所持有。juc中AbstractQueuedSynchronizer和AbstractQueuedLongSynchronizer继承了该类。
AbstractQueuedSynchronizer
AbstractQueuedSynchronizer是juc中一个十分重要的同步器,从下面这个图中我们可以看到ReentrantLock,ThreadPoolExecutor等一些比较常用的工具都是通过他来实现的。了解AbstractQueuedSynchronizer能帮助我们快速理解juc的其他工具。
作为一个同步器,AbstractQueuedSynchronizer同时提供了共享锁和独享锁的实现,AbstractQueuedSynchronizer通过内部类Node和ConditionObject来维护两个队列,队列的是由一个个Node组成,不熟悉Node的同学可以参考笔者的另一篇文章java.util.concurrent学习(七) AbstractQueuedSynchronizer 内部类Node ,ConditionObject,熟悉这两个内部类以后我们来看一下AbstractQueuedSynchronizer的两个队列:
API:
方法 | 描述 |
| 获取排他锁 |
| 可打断式的获取排他锁,即在获取排他锁的自旋过程中,一旦线程被打断,则抛出 |
| 可打断式的,可限制获取锁的超时时间的获取排他锁。 |
| 释放排他锁,唤醒头部的下一个线程。 |
| 获取共享锁 |
| 可打断式的获取共享锁 |
| 可打断式的,可限制获取锁的超时时间的获取共享锁。 |
| 释放共享锁 |
| 判断队列中是否仍有线程 |
| 查询是否有线程曾试图获取此同步器;也就是说,如果一个获取方法被阻塞了。 |
| 获取等待时间最长的线程 |
| 判断线程是否在队列中 |
| 判断是否有等待时间更长的线程 |
| 获取队列长度 |
| 获取队列中所有线程的集合 |
| 获取队列中所有排他线程的集合 |
| 获取队列中所有共享线程的集合 |
| 判断参数中的condition是否依赖于当前同步器 |
重点说一下获取锁的api:
1.acquire(int arg) 获取排他锁
public final void acquire(int arg) {
//如果获取不到排他锁,同时加入队列获取排他锁也失败
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//则打断当前线程
selfInterrupt();
}
/**
*添加节点
*/
private Node addWaiter(Node mode) {
//创建一个当前线程的节点,指定模式mode
Node node = new Node(Thread.currentThread(), mode);
//获取队尾
Node pred = tail;
//如果当前队尾不为空
if (pred != null) {
//则将该节点的前驱节点指向队尾节点
node.prev = pred;
//将该节点置为队尾节点并返回
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//如果当前队尾不为空,调用enq(final Node node)追加到队尾并返回
enq(node);
return node;
}
//追加队列
private Node enq(final Node node) {
//自旋
for (;;) {
Node t = tail;
//当队尾节点为空时,初始化头节点和尾节点,此时头尾节点相同
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
//当队尾节点存在时,将node追加到队尾并返回
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
//在队列中获取锁
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
//自旋
for (;;) {
//前驱节点
final Node p = node.predecessor();
//如果前驱节点是头节点,则获取锁
if (p == head && tryAcquire(arg)) {
//如果获取到了锁,则将当前节点置为头节点,并返回false
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//如果前驱节点不是头节点 或者没有获取到锁
//则判断node节点在获取排他锁失败后是否需要阻塞线程
//同时阻塞并返回线程是否已打断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
//如果无法获取到锁,则取消该节点
cancelAcquire(node);
}
}
//判断node节点在获取排他锁失败后是否需要阻塞线程
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//如果前驱节点的状态为SIGNAL,则返回true
return true;
if (ws > 0) {
//如果前驱节点的状态为取消,则不断获取前驱节点,并重置node的前驱节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//如果前驱节点的状态为0或者-3,则将前去节点的状态改为sinal
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
//阻塞当前线程,并返回线程是否已打断
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
//取消一个节点
private void cancelAcquire(Node node) {
//如果节点为空,则忽略
if (node == null)
return;
//将节点线程置为null
node.thread = null;
//跳过已取消的前驱节点
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
//获取后继节点
Node predNext = pred.next;
//将节点状态置为取消
node.waitStatus = Node.CANCELLED;
//如果该节点为队尾节点,则将前驱节点置为队尾
if (node == tail && compareAndSetTail(node, pred)) {
//将前驱结点的后继节点置为null
compareAndSetNext(pred, predNext, null);
} else {
int ws;
//如果前驱节点不是头节点,同时前驱节点的状态是signal或者将前驱节点从<0置为signal,同 //时前驱结点的线程不为null
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
//如果后继节点不为空且不是取消的
if (next != null && next.waitStatus <= 0)
//则将后继节点追加到前驱结点
compareAndSetNext(pred, predNext, next);
} else {
//否则唤醒后继节点
unparkSuccessor(node);
}
//该节点的后继节点指向自己,帮助gc快速回收
node.next = node; // help GC
}
}
//打断当前线程
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
看懂代码以后,我们用流程图来梳理一下获取排他锁的流程:
2.获取共享锁 acquireShared(int arg)
public final void acquireShared(int arg) {
//尝试获取共享锁
if (tryAcquireShared(arg) < 0)
//如果失败,则尝试不间断的获取共享锁
doAcquireShared(arg);
}
//不间断的获取共享锁
private void doAcquireShared(int arg) {
//创建一个共享节点并添加到队尾
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
//自旋
for (;;) {
//获取前驱结点
final Node p = node.predecessor();
//如果前驱结点是头节点
if (p == head) {
//尝试获取共享锁
int r = tryAcquireShared(arg);
//如果获取成功
if (r >= 0) {
设置头节点并传播
setHeadAndPropagate(node, r);
//前驱结点的后继节点指控,帮助gc快速回收
p.next = null; // help GC
//如果interrupted 为true,则打断当前线程,并退出自旋
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
//检查获取失败后是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
//如果需要,则阻塞该线程并判断是否被打断
parkAndCheckInterrupt())
//如果打断则修改局部变量interrupted
interrupted = true;
}
} finally {
//finally,如果获取失败,则取消该节点
if (failed)
cancelAcquire(node);
}
}
//设置头节点,并传播
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
//将该节点置为头节点
setHead(node);
//当以下任意一种情况出现时
//1.propagate 获取共享锁结果大于零
//2.头节点不存在
//3.头节点状态<0(signal,condition,propagate)
//4.新的头节点不存在
//5.新的头节点状态<0
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
//获取后继节点
Node s = node.next;
//如果后继节点不存在或者是共享的
if (s == null || s.isShared())
//
doReleaseShared();
}
}
private void doReleaseShared() {
//自旋
for (;;) {
//获取头部节点
Node h = head;
//如果队列存在
if (h != null && h != tail) {
int ws = h.waitStatus;
//如果头节点的状态是signal
if (ws == Node.SIGNAL) {
//将头节点从signa改为0,如果失败则重新开始检查
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
//释放后继节点
unparkSuccessor(h);
}
//如果头节点的状态是0
else if (ws == 0 &&
//则将其状态改为PROPAGATE,如果失败则重新开始
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
//如果头节点已改,则重新开始检查
if (h == head)
break;
}
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
//如果节点状态是负的,将状态改为0
compareAndSetWaitStatus(node, ws, 0);
//获取后继节点
Node s = node.next;
//如果后继节点不存在或者状态小于0
if (s == null || s.waitStatus > 0) {
s = null;
//从队尾开始不断向前获取不为空的节点直到该节点(但不包含该节点),将s指向该节点后最//近的一个非取消的节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//如果s节点存在
if (s != null)
//则唤醒s节点的线程
LockSupport.unpark(s.thread);
}
流程图:
参考:
AbstractQueuedSynchronizer源码解读