一、Node
在没有获取到锁时会将当前线程封装成一个node加入队列并循环获取锁
static final class Node {
//标记这是一个共享锁
static final Node SHARED = new Node();
//标记这是一个排它锁
static final Node EXCLUSIVE = null;
//线程是否已经终止/废除了
static final int CANCELLED = 1;
//标记当前节点需要被LockSupport.unpart()唤醒
static final int SIGNAL = -1;
//当前线程在条件队列
static final int CONDITION = -2;
//无条件传播(tryAcquireShared)
static final int PROPAGATE = -3;
//就是上边介绍的那几种状态(具体可以看源码介绍)
volatile int waitStatus;
//当前节点的前节点(用来判断前节点是否还在阻塞呢)
volatile Node prev;
//当前节点的下一个节点(将来用来唤醒下一个节点)
volatile Node next;
volatile Thread thread;
//节点锁模式(具体功用可查看源码)
Node nextWaiter;
//判断是共享锁还是排它锁
final boolean isShared() {
return nextWaiter == SHARED;
}
...
}
二、共享锁和排它锁
通过上面信息我们可以了解到锁有共享模式和排它模式,只需要传入对应的Node类型(Node.SHARED或者Node.EXCLUSIVE)。
常用的排它锁有ReentrantLock,读写锁的写锁
常用的共享锁有CountDownLatch,Semephore,读写锁的读锁等
三、排它锁加锁过程
拿ReentrantLock举例,调用lock();
public void lock() {
sync.lock();
}
默认是非公平锁:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
首先会调用compareAndSetState(0, 1),尝试获取锁:
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
如果cas成功,则代表获取到了锁,继续执行setExclusiveOwnerThread(Thread.currentThread());,将当前线程存入独占信息,然后返回,这是成功获取到了锁的过程。
如果失败则代表已经有其他线程获取到锁或者在竞争获取锁,调用acquire(1):
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这个方法是AQS里的方法啦,首先它先调用了tryAcquire(arg)尝试一下获取锁,如果这时候获取到了也直接返回(因为它拿到锁啦),失败后调用addWaiter将当前信息封装成Node存入等待Node链尾部,最后调用acquireQueued一直循环直到其拿到锁为止。
tryAcquire方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//首先获取到state,state为0代表没有线程获取到锁
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//state不为0,看当前线程是否之前已经获取到锁了(可重入)
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;
}
addWaiter
如果tryAcquire失败的话,首先会调用addWaiter(Node.EXCLUSIVE),传参为EXCLUSIVE,代表这是一个排它锁:
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return 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.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
就是一直for循环不断CAS尝试将当前node加入到队尾(如果没有头节点则先初始化一个)
acquireQueued
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
//无限循环直到return
for (;;) {
final Node p = node.predecessor();
//如果前节点是头节点,尝试获取锁,成功则返回啦
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//判断如果前节点被阻塞住了,当前节点也应该阻塞,以免占用CPU资源
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
主要是for循环一直尝试获取锁,当发现当前节点的前一个节点是head,也就是说当前节点是第一个有效节点,则尝试获取锁,如果成功获取到锁,则改变下head节点,然后返回了。
如果当前节点没有获取到锁,或者它的前节点不是head,会调用shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
这个方法主要就是判断当前节点的前节点,是否是Node.SIGNAL的,这个在刚开始讲到,这个标识是说明该节点是否已经被LockSupport.part()阻塞住了。
如果前节点是Node.SIGNAL,说明当前节点也应该阻塞(前节点都阻塞住了,自己再循环获取锁没有意义了,而且浪费CPU资源),调用parkAndCheckInterrupt:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
四、排它锁解锁
调用lock.unlock():
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//判断当前的线程是否有权限释放锁
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//重入锁有可能导致status释放一次还大于0
if (c == 0) {
free = true;
//去掉当前线程的排它锁状态,允许其他线程获取锁
setExclusiveOwnerThread(null);
}
//volatile修饰的元素保证其他线程可见
setState(c);
return free;
}
还记得之前获取锁时,只有当前节点才有机会tryAcquire吗?后续的节点都被LockSupport.part()阻塞住了,所以在释放锁后需要我们将后续节点唤醒:
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
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);
}
五、排它锁里的公平非公平模式
//公平锁
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//非公平锁
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
可见非公平锁就是在公平锁的基础上,首先尝试获取下锁,尝试失败之后的逻辑和公平锁一样~
六、排它锁里的尝试获取锁模式
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
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;
}
首先说,尝试获取锁逻辑是在非公平锁里实现的(就尝试获取一次,没有啥公平不公平的),直接cas尝试,获取到了就返回。
七、排它锁里的超时获取锁模式
主要是在阻塞等待节点时,调用了LockSupport.parkNanos(this, nanosTimeout)进行有时限的阻塞:
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
八、排它锁里的可中断获取锁模式
public final void acquireInterruptibly(int arg)
throws InterruptedException {
//如果线程中断,则直接抛异常
if (Thread.interrupted())
throw new InterruptedException();
//先尝试获取锁,doAcquireInterruptibly之前分析过了
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
//先封装节点,将节点加入到Node尾部
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
//这两个方法之前已经分析过了
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
其实doAcquireInterruptibly()跟之前分析的acquireQueued方法类似