通过学习ReentrantLock来学习AQS,本文主要是公平锁的上锁过程
AQS(AbstractQueuedSynchronizer)主要属性和内部类:
·
private transient volatile Node head; //队首
private transient volatile Node tail;//队尾
private volatile int state;//锁状态,加锁成功则为1,重入+1 解锁则为0
`
public class Node{
volatile Node prev;
volatile Node next;
volatile Thread thread;
}
0. 方法入口:
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock(true);//公平锁
lock.lock();
System.out.println("=====================");
lock.unlock();
}
进入lock方法,lsync.lock();再往下进到公平锁内部类的lock方法
public void lock() {
sync.lock();
}
进入acquire方法(父类AQS的acquire方法),会最先调用tryAcquire方法来尝试获取锁
回到ReentrantLock中tryAcquire方法的实现
1. 场景一:无线程竞争
- 首先获取当前线程
- 然后获取同步器的计算器state
- 当前场景没有线程占用,state为0,进入hasQueuedPredecessors方法(父类AQS的方法)判断当前线程要不要排队
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
AQS中tail、head默认初始都是null,所以h != t 直接返回false,tryAcquire方法去反了,所以执行CAS操作
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
CAS操作将state加1,就相当于加锁,然后setExclusiveOwnerThread把当前线程设置为独占锁的线程,返回true,上锁完成
2. 场景二:线程重入:
可重入:内部方法再次上锁可以上锁成功,即testMethod中可以lock成功
public class LockTest {
private static ReentrantLock lock;
public static void main(String[] args) {
lock = new ReentrantLock(true);
lock.lock();
testMethod();
lock.unlock();
}
private static void testMethod() {
lock.lock();
System.out.println("==================");
lock.unlock();
}
}
tryAcquire方法可以看出:
- main方法中上了锁后state为1,执行后续else if的逻辑,同一个线程,所以else if的判断成立,然后将state加1
- 返回true,上锁成功
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;
}