可重入锁,加锁方法源码跟进
进入加锁方法
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
reentrantLock.unlock();
}
进入 lock方法,以非公平锁实现为例子
lock方法
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
//进行cas操作,看当前线程是否已经锁了
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//进行获取锁操作
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
我们重点说下acquire方法(cas成功了,则说明无锁竞争)
进入acquire方法
public final void acquire(int arg) {
/**
*1.!tryAcquire(arg) 试图获取锁失败
*2.acquireQueued 加入获取锁队列,返回失败
*3.selfInterrupt 中断
**/
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
一个个讲解
进入tryAcquire方法(尝试获取锁)
以非公平锁的实现为例子
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//0 无锁
if (c == 0) {
//cas成功,可不管
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果线程现在标识是本线程则也可以进行获取锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
//此处重入次数过多会导致int溢出,所以需要进行健壮性判断,此处也说明ReentrantLockshi 是可重入锁,可多次获取属于本资源的锁
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
addWaiter加入等待队列
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 尝试直接将尾结点的下个节点指向当前节点,且修改尾结点
Node pred = tail;
if (pred != null) {
node.prev = pred;
//尝试对尾结点进行cas,失败则指向下面的enq方法
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//实现逻辑和上方一致,尾结点为空则初始化队列,然后尝试将当前节点加入队列,cas直到成功
enq(node);
return node;
}
acquireQueued()尝试获取线程所在的队列
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
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)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//1.shouldParkAfterFailedAcquire 检查前置节点状态是否正常
//2.parkAndCheckInterrupt 当前置节点状态正常,且不是头结点,当前节点执行状态挂起(可重入不用担心多次挂起)
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
//如有异常,本节点放弃进行加锁,将本节点的删除
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire方法,检查前置节点是否有效
/**
* 尝试获取锁失败后,检查和更新前驱节点状态
* 返回 true 后 将线程挂起. 此方法在acquireQueued中被循环调用,直到前驱节点为头节点或者获取锁成功
*
*/
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 {
/*
* 节点状态异常,cas尝试修改
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}