一、简介
AbstractQueuedSynchronizer类是一个极其重要的类,Lock.new Condition()返回的Condition对象就是AQS中的内部类。CyclicBarrier、CountDownLatch等类中的await方法就是调用的AQS类中的方法。
AQS有独占(只能一个线程运行,如ReentrantLock)和共享模式(多个线程运行,如Semaphore和CountDownLatch)。
二、源码中几个关键的部分
1、state:初始化为0,表示未锁定状态,state大于0,表示已上锁。state会累加,这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。 CountDownLatch的原理便是如此。任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。
2、内部类Node
AQS维护了一个内部类Node,用来封装每一个执行线程以及它的状态,如是否被阻塞,是否等待唤醒,是否已经被取消等。waitStatus用来区分当前节点的状态。
static final class Node {
//
static final Node SHARED = new Node();
//
static final Node EXCLUSIVE = null;
//线程等待超时或被中断,需要从同步队列中取消该Node的结点
static final int CANCELLED = 1;
//线程等待执行,上一个结点唤醒后就会唤醒当前结点
static final int SIGNAL = -1;
//与Condition相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁
static final int CONDITION = -2;
//与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态
static final int PROPAGATE = -3;
//当前结点的状态,共有上面4种状态 + 0,表示初始化状态
volatile int waitStatus;
//上一个结点
volatile Node prev;
//下一个结点
volatile Node next;
//当前线程
volatile Thread thread;
//condition队列中的下一个结点,与上面CONDITION这个状态有关
Node nextWaiter;
//
final boolean isShared() {
return nextWaiter == SHARED;
}
//获取上一个节点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() {}
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}
3、内部类ConditionObject
lock.newCondition()就是返回的这个对象。await方法,lock中condition.await,CyclicBarrier源码中的await都是调用的这个方法。内部有一个Node对象,当调用了conditon.await方法后,会创建当前Node节点,并添加到ConditionObject这个类中的lastWaiter,waitStatus置为-2,这样就又形成一个等待被signal唤醒的节点队列,这里称为condition队列。
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
三、共享模式流程
1、共享模式主要实现类是ReentrantReadWriteLock中的ReadLock。ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock();
2、首先调用tryAcquireShared尝试获取共享锁
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
//如果state不等于0且当前线程不是被阻塞的线程,就返回-1,表示不能获取共享锁
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//将state的值无符号右移16位
int r = sharedCount(c);
//判断头节点的下一个节点是否是共享模式,如果是共享模式,将state值加65535
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
//如果r等于0,当前线程就是头节点,count等于1
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
四、独占模式流程
1、创建对象Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();
2、线程1调用lock.lock方法,然后默认调用NonfairSync类的lock方法。(ReentrantLock默认非公平锁)。lock是可重入锁,第一次调用lock,会将Node中的state值从0变成1。如果又继续调用lock,会进入acquire(1)方法,将state再加1,变成2。以此类推。不过释放的时候不用释放多次,详情可见fullRelease方法。
3、如果还没被上锁,那么state变为1。
4、如果已经上锁了,会进入acquire方法,将当前节点添加到Node链表的tail处,随后Lock.park(this)。当被唤醒后,就轮到当前节点上锁了,如果tryAcquire成功,就把当前节点设置为头节点。至此,完成上锁的全程。
final void lock() {
//将state从0变为1,state是1表示没有加锁,否则表示加锁了
if (compareAndSetState(0, 1))
//将当前线程赋值给成员变量,这样别的线程要执行时可以用这个成员变量判断是否是上锁的线程,用来实现独占锁、重入锁
setExclusiveOwnerThread(Thread.currentThread());
else
//如果把state从0变成1失败,说明当前线程已经加过锁了。此时判断该线程是否等于exclusiveOwnerThread,如果是的话就把state再加1
acquire(1);
}
public final void acquire(int arg) {
//tryAcquire:尝试上锁。获取当前Node的state值,如果等于0,表示未上锁,就加锁。如果不是0,且也是阻塞的线程,就将state+1,若不是阻塞的线程,返回false。
//addWaiter:生成当前的Node节点对象,并添加到Node链表中的tail节点。
//获取当前节点的上一个节点,如果节点的waitStatus等于-1,表示需要唤醒,就阻塞住。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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;
}
//如果当前节点不具备上锁条件,那么就要判断是否应该阻塞(waitStatus是否等于-1)
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
5、调用condition.await(),先释放锁(严格讲是unpark头节点的下一个节点),再阻塞住当前节点。详细见下方代码。
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//将当前线程添加到ConditionObject类的Node节点队列中结尾处
Node node = addConditionWaiter();
//将state值置为0,并将Node中的头节点放行
int savedState = fullyRelease(node);
int interruptMode = 0;
//判断Node的waitStatus是否等于-2,如果是就阻塞。
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
final int fullyRelease(Node node) {
boolean failed = true;
try {
//得到节点的state值,正常情况下是1
int savedState = getState();
//
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
public final boolean release(int arg) {
//是否可以释放锁,如果可以,把exclusiveOwnerThread置为null,并把node中的state值变为0
if (tryRelease(arg)) {
//找到等待队列中的头节点,如果头节点不为null且不是空闲状态,就释放该节点锁。这个头节点是在第二个线程调用lock方法时才会赋值。
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
//将当前state的值减去传过来的参数,如果是0,则表示可以释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//如果当前await的线程不是lock的线程,就报错(await必须放在lock里)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//释放锁,将成员变量中的线程变量置为null,然后把当前线程的state变成0,表示释放锁
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
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.
*/
//如果下一个节点为null或处于被中断状态,就移除这个节点,把下一个处于阻塞状态的节点移过来
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);
}
6、condition.signal()。找到condtion队列中的头节点firstWaiter,并放到Node队列的tail中。signal释放锁是根据Node队列中节点的顺序释放的,是公平的。
private void doSignal(Node first) {
do {
//把头节点置为下一个节点
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
//把节点的waitStatus置为0
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//把这个节点添加到Node节点队列的tail中去
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
//这个CAS操作后,tail=node,但t还是等于原来的tail。因为改变的是tail指针存储的内存地址。
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
7、lock.unlock()。把Node中的head节点放行。
五、总结
AQS中有个state值,为0表示未上锁,大于0表示上锁了。ReentrantLock调用lock上锁时,state会加1,如果又调用lock,由于重入锁,state再加1。如果调用unlock,就将state减1,当减到0时,就将等待队列中的头节点释放锁。如果调用了condtion.await,就将该线程放入等待队列中,把state值置为0,ExclusionThread置为null,然后阻塞住线程,并释放等待队列中头节点的线程。