AQS:AbstractQueuedSynchronizer,抽象队列同步器。
在Java并发包中提供的锁(java.util.concurrent.locks),都是利用 AQS 来实现的。AQS底层其实也是利用 CAS 来共同实现锁的机制。
AQS 内部核心的参数:
state:用于记录锁的同步状态,AQS底层的核心字段。但是,在ReentrantLock、ReentrantReadWriteReadLock的用法又是不太一样的。
head:如果线程获取锁失败,会进入队列等待,其中head指针就是指向等待队列的对头。
tail:tail指针指向等待队列的队尾。
head 指针和tail指针的类型都是 Node,Node是AQS的静态内部类,用于实现等待队列。Node主要核心参数有:waitStatus-等待状态、prev指针、next指针、thread-指向等待线程。因为有prev指针和next指针,所以这个等待队列是双向列表。
各种offset:stateOffset、headOffset、tailOffset、waitStatusOffset、nextOffset。这些offset主要用于CAS操作更新字段值,每个offset会记录对应字段在类中的偏移量。在AQS中,会利用CAS来实现无锁化更新字段值。
unsafe:Unsfase 实例,用于执行CAS操作来更新字段值。
exclusiveOwnerThread:这个是AQS的父类(AbstractOwnableSynchronizer)的字段,用于标识当前成功持有锁的线程。
值得注意的是,不管是state变量、head指针,还是tail指针,他们都是利用 volatile 来修饰,来保证多线程访问中变量的可见性和一致性。
ReentrantLock 中有一个抽象静态内部类:Sync,它继承了 AQS,重写了AQS的某些方法:如tryRelease、isHeldExclusively。
接着,因为ReentrantLock提供了公平锁和非公平锁,所以内部有两个静态内部类,都是实现了抽象静态内部类 Sync:NoFairSync和FairSync,分别代表非公平锁和公平锁。
不管是 FairSync 还是 NoFairSync,都是利用 AQS 中的 state 来表示锁的持有状态。只要 state >= 1,即现在已经有线程在持有锁,否则等于0就是没有线程持有锁。当线程成功持有锁,即成功将 state 设置成1,那么就会将 exclusiveOwnerThread 指向当前线程,这样也可以支持锁可重入,只要获取锁的线程就是 exclusiveOwnerThread 指向的线程,那么就直接给 state 再加1就可以了,来表示当前线程再次成功获取锁。
不管是 ReentrantLock 中,公平锁和非公平锁的加锁逻辑会有点不一样。
非公平锁=NoFairSync:会直接尝试利用CAS将 state 从0设置成1,如果设置成功,则表示获取锁成功,此时会将 exclusiveOwnerThread 指向当前线程。否则,会调用 acqiure(1) 方法,底层是调用 AQS 的 acquire 方法。
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
公平锁=FairSync:直接调用 acquire(1) 方法,底层也是调用 AQS 的 acquire 方法。
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
方法比较好理解,就是调用 tryAcquire(int arg) 方法去尝试获取锁,而这个 tryAcquire 方法需要 NoFairSync 和 FairSync 自行实现。
在 ReentrantLock 中,公平锁和非公平锁的加锁逻辑是有点不太一样的。
首先是NoFairSync:非公平锁最后是调用自身的 nonfairTryAcquire 方法,里面逻辑很简单。
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;
}
接着是FairSync:这里和非公平锁不同的一点是,如果state=0,还需要判断等待队列中是否有线程在等待,如果没有才可以去尝试利用 CAS 去将state从0设置成1,如果成功就设置 exclusiveOwnerThread 为当前线程,然后返回true表示获取锁成功。如果state不等于0,和非公平锁一样,进行重入的逻辑。
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;
}
如果获取成功,那么就直接跳出if分支了;否则表示获取锁失败,会调用 addWaiter 方法进入等待队列,然后调用 acquireQueued 来进入死循环等待获取锁。
但是在 acquireQueued 方法中,肯定不是一直循环获取锁啦,在死循环中会有下面额逻辑
ReentrantLock 释放锁就不分公平和非公平了,所以 ReentrantLock 的释放锁是直接调用 AQS 的 release(int arg) 方法。方法很简单,下面讲解一下。
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;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
这里很简单,只是将state-1,并且要判断是否存在重入锁的情况。如果不存在,将 exclusiveOwnerThread 设置为null,然后直接返回true表示释放锁成功。否则只是将state-1,返回返回false,表示这个锁还没真正被释放掉。