AQS简介
AQS 的全称为(AbstractQueuedSynchronizer),这个类在 java.util.concurrent.locks 包下面。
AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的 ReentrantLock,Semaphore,其他的诸如 ReentrantReadWriteLock,SynchronousQueue,FutureTask(jdk1.7) 等等皆是基于 AQS 的。当然,我们自己也能利用 AQS 非常轻松容易地构造出符合我们自己需求的同步器。
AQS源码
加锁
- 通过ReentrantLock 进行分析。
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock(); //获得锁
进入lock方法(先看一下NonfairSync非公平锁)
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
compareAndSetState(0,1)就是一个CAS操作,点进去可以看到是对state做的cas操作,如果state=0就替换成1,并且拿到独占锁。
通过cas没有拿到锁是acquire(1)方法:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire(int arg)这个方法包含三个子方法,我们逐个看一下。(黑色字体)
看第一个方法:
final boolean nonfairTryAcquire(int acquires) {
//拿到当前线程
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//state == 0证明锁已经释放,通过cas再次尝试获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// state != 0证明没有释放锁,判断当前线程是不是独占锁的线程,是的话,则重入,此处体现重入性锁
else if (current == getExclusiveOwnerThread()) {
// 重入锁state + 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//既没有释放锁,又不是重入锁,则此线程拿不到锁
return false;
}
看第二个方法
private Node addWaiter(Node mode) {
//AQS核心结构就是双向链表,把当前线程放入链表一个节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
//第一个线程进来以后,链表的tail尾节点是null,当然头节点也是null的。整个链表还是没有构建的,当第二个线程 进来以后,走下面的if判断。下面的逻辑就是把新进来的线程放到队列的后面。
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
来看一下enq(node);
private Node enq(final Node node) {
//自旋
for (;;) {
Node t = tail;
//如果尾节点是null,则创建一个新的链表,尾节点=头节点
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
//如果尾节点不是null,则向链表的尾部加入该线程
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
需要注意的是,上面的方法返回的都是当前线程所在的node节点。
第三个方法:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
//注意每一个自旋
for (;;) {
//该方法就是获取当前线程节点的上一个节点 = p
final Node p = node.predecessor();
//p 节点是头节点的话,尝试tryAcquire()再次获取锁
if (p == head && tryAcquire(arg)) {
//p拿到锁以后,把当前线程所在节点放到头节点,此时p就是拿到锁的线程。把p从AQS双向链表中剔除
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//p不是头节点,或则trtAcquire没有拿到锁,就会把当前线程尝试做一个挂起的操作。具体看下面方法的源码
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire(p, node)
// pred是上一个节点,node是当前线程所在的节点,该方法就是需要把当前线程的上个节点的waitStatus赋值为SINGAL
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//上一个线程等待状态
int ws = pred.waitStatus;
//
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
//ws > 0只有一种情况就是 CANCELLED = 1,该种状态就是异常的线程节点会是CANCELLED,现在需要把异常的线程节点去掉
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 当pred的waitStatus更改为SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
//这句后面线程就是挂起了,不再往下面执行了。需要等待LockSupport.unpark()对该线程做一个释放。
LockSupport.park(this);
return Thread.interrupted();
}
此处,需要说一下***Thread.interrupted()***,当时看了好久没明白。
正常情况下这个Thread.interrupted() 返回的是false.但是这个线程如果被中断过,再去执行这个方法就是true并且线程复位。
回到调用parkAndCheckInterrupt方法的地方
parkAndCheckInterrupt() 方法返回true的情况就是线程被中断了,先复位,然后在调用selfInterrupt();进行中断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt());
interrupted = true;
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
以上就是***reentrantLock.lock();***非公平锁的源码。
解锁
reentrantLock.unlock();
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
看一下***tryRelease(arg)***方法
//就是把state字段做减的操作
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;
}
unparkSuccessor()方法
这个方法就是做一个unpark操作,从链表的尾部开始遍历。
private void unparkSuccessor(Node node) {
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.
*/
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);
}
以上就是解锁的过程。
公平锁与非公平锁的体现
//非公平锁
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 公平锁
final void lock() {
acquire(1);
}
第一个区别就是:非公平锁进来一个线程就会通过CAS去抢一次锁,公平锁不会
还有一个区别就是: 公平锁会判断当前的双向链表中是否还有节点,非公平锁不会。