前言
park 和 unpark:
LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语。LockSupport提供的两个主要方法就是park和unpark。park我们可以理解为挂起,而unpark我们理解为唤醒。 LockSupport同步线程和wait/notify不一样,LockSupport并不需要获取对象的监视器,而是给线程一个“许可”(permit)。而permit只能是0个或者1个。unpark会给线程一个permit,而且最多是1;而park会消耗一个permit并返回,如果线程没有permit则会阻塞。LockSupport.unpark(“线程名”);
AQS双向链表的节点node:
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
lock 过程
public void lock() {
sync.acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
公平锁和非公平锁在这里的差别就是,公平锁在加锁之前会去判断:hasQueuedPredecessors 是否有前node,也就是需不需要排队。非公平锁不会进行判断,直接通过CAS进行尝试加锁。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
- 首先是没有线程获取锁的情况。假如这时来了一个线程A加锁,发现 state == 0 ,然后会判断自己是否需要排队,这里不需要排队。这时候线程A会通过CAS的方式,去修改锁的状态。然后设置exclusiveOwnerThread(当前持有锁的线程) 为线程A。
protected final boolean compareAndSetState(int expect, int update) {
return STATE.compareAndSet(this, expect, update);
}
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
- 当线程A持有锁后,这时候又来了一个线程B尝试加锁。线程B发现锁的状态并不是0,并且当前持有锁的线程也不是自己,那么线程B则会进行入队操作。也就是执行下面的 addWaiter 和 acquireQueued 方法。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
addWaiter : 这里是一个自旋的操作,因为线程A加锁的时候并不需要入队,所以 tail 现在为 null 。那么首先就会进入 else 中去 initializeSyncQueue 。这里第一次进队列,会new 一个 线程为null 的node,并且把head 和 tail 都设置成这个 node。然后再把线程B 生成的node,维护成 null线程 node的下一个节点,并且把 线程B node 设置成 tail。
ps : 双向链表的第一个node,永远是线程为null的,获取当前锁的线程不会在双向链表中。
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
initializeSyncQueue();
}
}
}
/**
* Initializes head and tail fields on first contention.
*/
private final void initializeSyncQueue() {
Node h;
if (HEAD.compareAndSet(this, null, (h = new Node())))
tail = h;
}
acquireQueued : 这个方法主要的作用就是去阻塞获取不了锁的线程B。这里又是自旋操作,首先会去判断一下线程B节点的上一个节点是不是head,如果是head那么会再尝试去加锁 tryAcquire(因为有可能执行到这里线程A已经释放锁了,但是还没唤醒下一个。只有当前节点的上一个节点是head才会去尝试 tryAcquire)。然后就是比较难的shouldParkAfterFailedAcquire方法了。
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
shouldParkAfterFailedAcquire :设置前一节点的waitStatus为 SIGNAL(-1),并且再次回到acquireQueued再进行一次tryAcquire,然后回到shouldParkAfterFailedAcquire,这时满足if (ws == Node.SIGNAL),然后返回 true 。然后执行 parkAndCheckInterrupt。
Node有5中状态,分别是:CANCELLED(1),SIGNAL(-1)、CONDITION(-2)、PROPAGATE(-3)、默认状态(0)
CANCELLED :在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点, 其结点的waitStatus为CANCELLED,即结束状态,进入该状态后的结点将不会再变化
SIGNAL :只要前置节点释放锁,就会通知标识为SIGNAL状态的后续节点的线程
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt :park线程B。线程B执行到这里就暂时挂起了。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
这里可以先去看下面的 unlock过程
然后被唤醒了执行 return Thread.interrupted(),然后就回到了 acquireQueued 方法中的自旋代码里。这时候就会加锁成功,然后执行 setHead(node);
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
setHead :会把当前线程,也就是线程B的这个node中的 thread 设置成null,并且把这个node设置成head,并且把之前的head出队。所以说持有锁的线程不会在双向链表中。
/**
* Sets head of queue to be node, thus dequeuing. Called only by
* acquire methods. Also nulls out unused fields for sake of GC
* and to suppress unnecessary signals and traversals.
*
* @param node the node
*/
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
unlock 过程
public void unlock() {
sync.release(1);
}
因为之前线程B进入了双向链表,在shouldParkAfterFailedAcquire方法中修改了head节点中的waitStatus,所以会去唤醒。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
释放锁,如果states为0 ,设置当前持有锁的线程为null。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
唤醒后继节点,也就是可以唤醒之前的park的线程B了。
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
// 如果ws < 0表明是正常状态,置为0表示不需要唤醒后继节点这类操作
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
// ws大于0表示已经取消了,那就找到第一个未取消的节点来唤醒,不过这里是通过从后往前扫的方式处理的
// 通常情况下是唤醒node.next节点,但是考虑到节点被取消或者是null的情况,需要从后面往前扫
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 唤醒线程
if (s != null)
LockSupport.unpark(s.thread);
}