ReentrantLock之非公平锁加锁流程
目录
一、非公平锁和AQS的关系
二、非公平锁加锁流程
2.1 t1第一次加锁
final void lock() {
//第一次加锁 if条件成立,加锁成功
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
情景:t1线程第一次加锁,没有加锁之前AQS(NonfairSync)的状态
图1
t1加锁后AQS(NonfairSync)的状态
图2
2.2 t2第二次加锁
情景:t2线程来加锁如果t1没有释放,AQS(NonfairSync)的状态和图2一样。
2.2.1 t2加锁失败
代码:
@Slf4j(topic = "liheng")
public class Lock1 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(()->{
lock.lock();
try {
log.debug("t1执行------------");
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
},"t1").start();
new Thread(()->{
lock.lock();
try {
log.debug("t1执行------------");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
},"t2").start();
}
}
代码调用流程:
//1. Lock1.java 31行
lock.lock();
//ReentrantLock.java
public void lock() {
sync.lock();
}
//2. ReentrantLock.java
abstract void lock();
//3.ReentrantLock.java
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
//if条件不成立
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);//第二次加锁执行
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//4. AbstractQueuedSynchronizer.java
/**
4.1 tryAcquire(arg)尝试获取锁失败,应为t1睡眠时间100秒
4.2 addWaiter(Node.EXCLUSIVE) 维护AQS队列,调用enq()方法
4.3 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
4.4 parkAndCheckInterrupt() 调用LockSupport.park(this);t2线程入队,最终结果就是图3所示
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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;
}
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;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
t2加锁失败之后,AQS(NonfairSync)的状态
图3
2.2.2 t2加锁成功
场景:t2要加锁成功的前提是t1释放锁,那么当前AQS(NonfairSync)的状态如图1所示。
那么当t2加锁成功之后,AQS(NonfairSync)的状态如下图所示:
图4
结论:ReentrantLock,如果线程之间没有竞争,性能很高,因为都不需要初始化队列,永远和第一次加锁一样。
2.3 t3来加锁
情景:t1没有释放锁,t2在排队,t3现在来加锁
t3加锁结束之后的AQS(NonfairSync)的状态如下图所示:
图5