我的原则:先会用再说,内部慢慢来
一、竞争锁的场景
- 只有一个线程:
1.1. threadA 首次直接成功 lock
1.2. threadA 重入 - 存在多个线程竞争(假设2个线程,threadA + threadB: threadA 目前持有锁,threadB 准备来抢 lock)
2.1 threadB进入等待队列后,发现threadA 还没干完活,threadB 阻塞
2.2 threadB进入等待队列后,发现threadA 已经干完活来,threadB 直接拿到
二、代码Hirerachy结构
架构代码:
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
abstract static class Sync extends AbstractQueuedSynchronizer {}
static final class NonfairSync extends Sync {}
static final class FairSync extends Sync {}
}
三、相关对象展示
ReentrantLock lock = new ReentrantLock();
- ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
/**
* ====================== 抽象静态内部类 sync ======================
**/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
final boolean nonfairTryAcquire(int acquires){}
protected final boolean tryRelease(int releases) {}
protected final boolean isHeldExclusively() {}
final ConditionObject newCondition() {}
final Thread getOwner() {}
final int getHoldCount() {}
final boolean isLocked() {}
private void readObject(java.io.ObjectInputStream s){}
}
/**
* ====================== 公平锁与非公平锁实现抽象静态内部类 sync ======================
**/
static final class NonfairSync extends Sync {
final void lock() {}
protected final boolean tryAcquire(int acquires) {}
}
static final class FairSync extends Sync {
final void lock() {}
protected final boolean tryAcquire(int acquires) {}
}
/**
* ====================== ReentrantLock 构造方法 ======================
**/
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
* ====================== ReentrantLock 下面的几个方法都是基于 sync ======================
**/
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
public int getHoldCount() {
return sync.getHoldCount();
}
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
public boolean isLocked() {
return sync.isLocked();
}
public final boolean isFair() {
return sync instanceof FairSync;
}
protected Thread getOwner() {
return sync.getOwner();
}
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
public final int getQueueLength() {
return sync.getQueueLength();
}
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}
}
- AbstractQueuedSynchronizer
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer implements java.io.Serializable {
private transient volatile Node head;
private transient volatile Node tail;
private volatile long state;
static final class Node {
static final Node SHARED = new Node(); //共享模式
static final Node EXCLUSIVE = null; // 独占模式
static final int CANCELLED = 1 (处于取消的等待状态。因为超时或中断就会处于该状态,并且处于该状态的节点不会转变为其他状态处于该状态的节点不会再次被阻塞)
static final int SIGNAL = -1;(等待状态。表示后继节点是否需要被唤醒)
static final int CONDITION = -2;(Condition状态。该节点处于条件队列当中,该节点不会用作同步队列直到设置状态0用来传输时才会移到同步队列当中,并且加入对同步状态的获取)
static final int PROPAGATE = -3;(迭代状态。表示下一次共享式同步状态获取将会无条件地传播下去)
volatile int waitStatus; // 等待状态
volatile Node prev; //前驱指针
volatile Node next; // 后继指针
volatile Thread thread; //当前Node指向的thread
Node nextWaiter; // 这个当 EXCLUSIVE 的时候,始终是null。它使用在 Condtion中
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;
}
}
}
- AbstractOwnableSynchronizer
@Data
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
private transient Thread exclusiveOwnerThread;
}
四、lock 源码 ,根据场景剖析加锁 (以非公平锁为例)
1.1 threadA 首次直接成功 lock
class NonfairSync extends Sync {
final void lock() {
// 必须在队列为空的情况下,才能执行这个操作。CAS 设置状态 1
if (compareAndSetState(0, 1))
// 获取到lock了,当前线程设置为独占线程
setExclusiveOwnerThread(Thread.currentThread());//AbstractOwnableSynchronizer#setExclusiveOwnerThread
/*
======== 1.1. 首次直接成功 lock ,程序跑到这里,不往下走了 ========
*/
else
// 获取不到锁,走这里
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
1.2 threadA 重入
当前 lock.state >= 1,并且lock.currentThread = this, 走acquire(1)
- 看下 acquire 方法:
void AbstractQueuedSynchronizer#acquire(int arg) {
// 下面 &&符号表示: tryAcquire 获取不到锁的时候,才继续往下走
if (!tryAcquire(arg) &&
/*
======== 1.2. 重入成功 lock ,程序跑到这里 tryAcquire 返回,不往下走了========
*/
/*
======== 2.1. 同一个 lock 对象,thread1 拿住了这个lock,thread2 准备来抢夺 ========
*/
// 下面有俩方法: AbstractQueuedSynchronizer#acquireQueued 与 AbstractQueuedSynchronizer#addWaiter
acquireQueued(
// Node EXCLUSIVE = null
addWaiter(Node.EXCLUSIVE), arg)
)
selfInterrupt();
}
- 看下 tryAcquire 方法:
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
AbstractQueuedSynchronizer#tryAcquire 必须后续实现,现在看 NonSync#tryAcquire
boolean NonfairSync#tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
- 看下 nonfairTryAcquire 方法:
boolean Sync#nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取 state,未设置,默认是 0
int c = getState();
// 1.2. 重入成功 c >=1 , 不走下面的if
if (c == 0) {
// CAS 设置状态,跟上面的 NonfairSync#lock() 方法其实是有代码冗余
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
// 获取到锁,返回true,非重入。外面acquire就不往下走了
return true;
}
}
// 1.2. 重入成功 c >=1 , 跳过上面的if ,状态+1
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
// 获取到锁,返回true,重入。外面acquire就不往下走了
return true;
}
return false;
}
2.1 threadB进入等待队列后,发现threadA 还没干完活,threadB 阻塞
- 查看 acquire 方法 ,!tryAcquire(arg) 返回 false,跑
acquireQueued(
addWaiter(Node.EXCLUSIVE), arg) // NodeEXCLUSIVE = null
)
- 看 addWaiter 方法
Node AbstractQueuedSynchronizer#addWaiter(Node mode) {
// Node.EXCLUSIVE = null
Node node = new Node(Thread.currentThread(), mode);
// 一开始 tail = null , 所以 pred 也是 null
Node pred = tail;
/*
1. 等待队列里面最少有一个Node,Node 加入到队列后面。
2. 等待队列里面一个Node都没有,那么 pred = null ,直接 enq(node)
*/
if (pred != null) {
node.prev = pred;
// 将新进来的 Node 接到队列最后面
if (compareAndSetTail(pred, node)) {
pred.next = node;
// 返回传进来的节点
return node;
}
}
/*
enq 进队列,
1. 队列是空 :创建队列,也就是整个队列都没Node的时候
2. 队列不为空:那么跑到这一步的原因是上面setTail失败了,也就是该node本该插入到队列末端,但是没有。因为其他线程已经领先执行该操作了。为了继续往队列的尾巴插入,调用了 enq方法。
*/
enq(node);
// 返回准备入队列的 node
return node;
}
AbstractQueuedSynchronizer.Node#Node(Thread thread, Node mode){ // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
- 看 enq 方法
Node AbstractQueuedSynchronizer#enq(final Node node) {
for (;;) {
/*
1. 队列里面是空:
1.1 for 第一次循环 tail = null, 所以 t = null;
1.2 for 第二次循环 t = tail,tail 这个时候指向了 new Node(),所以 t 指向了 new Node()
2. 队列不为空,里面只一个有 NodeB
2.1 for CAS操作,把NodeC 插入到 NodeB 后面
*/
Node t = tail;
if (t == null) {
// CAS 设置 head 指针,head 指向 new Node()
if (compareAndSetHead(new Node()))
// tail 也指向 new Node()
tail = head;
} else {
/*
1. 队列里面是空:
1.1 初始化head和tail后,第二次 for 的时候进入了。
1.2. 现在准备入队列的 node 的前指针 prev 指向了 t,也就是 new Node()
2. 队列不为空,只一个有 NodeB,那么NodeC 插在NodeB的后面
*/
node.prev = t;
// CAS 设置 tail 指向了准备入队列的 node,如果插入失败就下个for继续插入。
if (compareAndSetTail(t, node)) {
// t 的后指针 next 指向了 node
t.next = node;
// node前面的一个node返回,最前面是Node new,这个enq方法的返回值在 addWaiter 这一步骤没被使用。
return t;
}
}
}
}
- addWaiter(Node.EXCLUSIVE), arg) 方法结束,进入 acquireQueued方法
- 看 acquireQueued 方法:
boolean AbstractQueuedSynchronizer#acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
/*
AbstractQueuedSynchronizer.Node#prev
1. 当 queue里面只有 new node + 当前 nodeB 的时候,
p = prev 指向了 new node ,和 head一样,所以进入下面的方法。
*/
final Node p = node.predecessor();
/*
tryAcquire :
1. true 表示刚刚占据lock的threadA,已经处理完毕释放了lock ,this是threadB,现在有机会获取锁了。(debug的时候出现几率多,因为停留太长时间了,threadA已经搞完事情了)
2. false 表示刚刚占据lock的threadA现在还占用着,所以走 shouldParkAfterFailedAcquire
*/
// 2.2 前驱p是head才可以尝试去 tryAcquire 获取锁,否则只能走下面去进队列阻塞
if (p == head && tryAcquire(arg)) {
// head 指向nodeB,释放 nodeB 的前驱指针 prev nodeNew
setHead(node);
p.next = null; // help GC p == head指向的临时节点 new Node() 的后驱指针next指到 null
failed = false;
// return false,外面不往下走了
return interrupted;
}
// 2.1 threadB 阻塞等待拿 lock
// tryAcquire 返回 false ,第一次循环进入 shouldParkAfterFailedAcquire 返回了 false, pred指向的Node(new) ,waitStatus 从 0 变成了 1
if (shouldParkAfterFailedAcquire(p, node) &&
/*
tryAcquire 返回 false , pred指向的Node(new) 的 ,waitStatus现在是1了,shouldParkAfterFailedAcquire 返回了 true。
可以进入 parkAndCheckInterrupt ,这个方法会一直阻塞,直到拿到 lock,
LockSupport.park(this) 会阻塞线程,直到被叫醒: unlock的时候,LockSupport.unpark(s.thread);
*/
// 如果线程在这里被打断,那么无所谓,只是标记一下,然后重新进入 for 循环去获取锁,拿不到继续阻塞。
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
-
===== 场景2.1 ===== 进入下面代码段
-
看下代码段 (2.1 threadB 阻塞等待拿 lock 走到了这里 )
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
-
看下 shouldParkAfterFailedAcquire 方法:
boolean AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire(Node pred, Node node) {
// 前驱指针的状态,此时应该是 new node(),waitStatus初始化默认是 0
int ws = pred.waitStatus;
//状态为-1,表示后继节点已经处于waiting等待状态,等该节点释放或取消,就会通知后继节点
if (ws == Node.SIGNAL)
return true;
//如果状态大于0--取消状态,就跳过该节点循环往前找,找到一个非cancel状态的节点
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
//赋值pred的后继节点为node节点
pred.next = node;
} else { //如果状态小于0
//必须是PROPAGATE或者0--表示无状态,当是-2的时候,在condition queue队列当中
//通过CAS设置pred节点状态为signal
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
- 看下 parkAndCheckInterrupt 方法:
能跑到这里说明shouldParkAfterFailedAcquire返回了true,也就是 pred.waitStatus = Node.SIGNAL,也就是后继节点需要被唤醒。successor’s thread needs unparking
boolean AbstractQueuedSynchronizer#parkAndCheckInterrupt() {
//通过LockSupport工具阻塞当前线程
LockSupport.park(this);
return Thread.interrupted();
}
void LockSupport#park(java.lang.Object)(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L); // 这个地方调用系统方式,直接阻塞停下来,等待 unpark
setBlocker(t, null);
}
void LockSupport#setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
- 再来一个 NodeC,直接插入到NodeB 后面
自此, ===== 场景2.1 ===== 分析完毕。
2.2 threadB进入等待队列后,发现threadA 已经干完活了,threadB 直接拿到
threadB 进入的时候,首先进入阻塞。然后 threadA 跑完了,threadB tryAcquire 拿到锁,返回
- ===== addWaiter===== 方法threadB没拿到锁,进入 acquireQueued方法
- ===== acquireQueued 方法 ===== 在内部的 ===== tryAcquire 方法 ===== 拿到了锁
if (p == head && tryAcquire(arg)) {
// head 指向nodeB,释放 nodeB 的前驱指针 prev nodeNew
setHead(node);
p.next = null; // help GC p == head指向的临时节点 new Node() 的后驱指针next指到 null
failed = false;
// return false,外面不往下走了
return interrupted;
}
- 看下 setHead 方法:
void AbstractQueuedSynchronizer#setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
若是NodeB 进来,那么NodeB 的当前thread就变成了 null了,他变成头节点了。因为刚刚threadB 在tryAcquire 的时候已经获取到锁了。
自此, ===== 场景2.2 ===== 分析完毕。
五、unlock源码(以非公平锁为例)
场景: threadB,threadC 正在阻塞,threadA干完了,准备unlock
- 看下代码 lock.unlock
void ReentrantLock#unlock() {
sync.release(1);
}
boolean AbstractQueuedSynchronizer#release(int arg) {
if (tryRelease(arg)) {
/*
在没有重入的情况下, tryRelease 返回 true,往下走
*/
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
boolean ReentrantLock.Sync#tryRelease(int releases) {
int c = getState() - releases;
// 只有获取锁才有资格释放锁,否则抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
/*
在没有重入的情况下, getState() == 0,c = 0
*/
if (c == 0) {
free = true;
// 清空参数
setExclusiveOwnerThread(null);
}
/*
在没有重入的情况下, c = 0
*/
setState(c);
return free;
}
- 看下 AbstractQueuedSynchronizer#unparkSuccessor 方法
void AbstractQueuedSynchronizer#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;
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.
*
* 1. unpark的thread正常的话是 head的下一个节点,目前就是 NodeNew的下一个节点。
* 2. 但是如果Node的等待状态被取消cancelled(一般是lock.lock(10),等待时间到了),或者是 node == null,
* 那么会从 tail 开始往前面找,找到 waitStatus <= 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);
}
- threadA unlock方法之后,释放锁:
- threadB 被唤醒,去拿锁。
===== acquireQueued 方法 ===== 在内部的for 循环 拿到了锁
========= 流程跑完 =========
六、lockInterruptibly 源码剖析
- 看下 lockInterruptibly 方法
public void ReentrantLock#lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
- 看下 acquireInterruptibly 方法
先对比下【场景2.1】 的代码
public final void AbstractQueuedSynchronizer#acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
- 看下 doAcquireInterruptibly 方法
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
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())
// 区别就在这,lock.lock() 方法的话,只是在这里标志一下 interrupt = true,然后继续进入for循环去拿锁,然后继续阻塞,但是 doAcquireInterruptibly 这个时候会抛异常,暂停阻塞。
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
看下图,就这么一个区别,就是被interrupt的时候,接下去如何应对。
七、trylock 源码剖析
1. tryLock()
public boolean ReentrantLock#tryLock() {
return sync.nonfairTryAcquire(1);
}
走的是 nonfairTryAcquire 方法 ,那么就只有一次获取锁的机会,要么成功,要么失败,不进入队列。
2. tryLock(long, java.util.concurrent.TimeUnit)
- 看下 trylock 方法
boolean ReentrantLock#tryLock(long timeout, TimeUnit unit)throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
- 看下 tryAcquireNanos 方法
public final boolean AbstractQueuedSynchronizer#tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
- 看下 doAcquireNanos 方法
先对比下【场景2.1】的代码
private boolean AbstractQueuedSynchronizer#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);
}
}
看图对比一下 acquireQueued 方法和 doAcquireNanos 方法:
区别1 就在 doAcquireNanos 方法,接受打断Interrupt,这一点跟 lockInterruptibly 一样
区别2 就在 doAcquireNanos内部 LockSupport.parkNanos(this, nanosTimeout); 指定了阻塞时间。等被唤醒或者自动醒来的时候,如果被打断,那么抛异常,没被打断就重新进入 for循环,如果时间到了,那么直接return false,表明没拿到锁。
3. .LockSupport#park 解析
LockSupport.park() 是一个静态方法,可以用于让当前线程挂起(即阻塞当前线程),在挂起期间,线程将处于休眠状态,不会占用 CPU 资源。
- 退出时机如下:
1、调用该方法的线程被中断、
2、调用 unpark(Thread.thread) 方法并指定当前线程为参数、
3、使用 parkNanos(long nanos) ,指定挂起时间,时间到了,就会唤醒
八、番外篇
下一章节:【线程】ReentrantLock + Condition 源码剖析 (九)
上一章节:【线程】ReentrantLock 实战 (七)