一、AQS原理
同步状态State+ 同步等待队列
如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态(cas状态);如果共享资源被占用,就需要一定的阻塞等待唤醒机制(LockSupport.park跟unpark)来保证锁分配。
private volatile int state; #临界状态
#用CAS的原子性操作state
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
private transient Thread exclusiveOwnerThread;//代表抢占到锁的线程
#核心内部类 -组成双向同步队列
static final class Node {
volatile Node next;//后一个节点
volatile Node prev;//前一个节点
volatile Thread thread;
volatile int waitStatus;//线程内部状态
}
private transient volatile Node head;//同步等待队列头
private transient volatile Node tail; //同步等待队列尾
public final void acquire(int arg) {
if (!tryAcquire(arg) && //尝试获取锁。tryAcquire为抽象方法,由子类方法实现
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) #如果获取锁失败,addWaiter把该线程加入到同步队列。acquireQueued调用park方法阻塞自己
selfInterrupt();
}
acquire()包含三个重要的方法。tryAcquire()由子类实现,为模板设计模式。addWaiter()把当前线程封装成一个节点,并加入到等待队列中。
acquireQueued()不断尝试获取锁,若获取失败,则调用park阻塞自己,直到获取到锁。
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)) {//如果队列非空,把node节点加到队列尾部
pred.next = node;
return node;
}
}
enq(node);//如果队列头是空的,则先创建队一个哨兵节点作为队列头,再把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()) #调用park方法阻塞自己
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
public final boolean release(int arg) {
if (tryRelease(arg)) { //尝试释放锁。释放成功,继续往下走
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//调用unpark方法唤醒同步队列中的第一个等待线程
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
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);//调用unpark唤醒队列头的线程
}
二、ReentrantLock源码
1.如图,为ReentrantLock与AQS的关系。ReentrantLock有一个Sync属性,而Sync最终继承自AQS,可以理解为ReentrantLock含有一个AQS属性。
2.ReentrantLock里面有一个Sync变量。lock()方法最终调用的都是Sync#lock()。而Sync#lock()方法最终调用的AQS#acquire()方法。
3.同时要注意,AQS#acquire()方法暴露一个抽象方法tryAcquire()由子类具体实现。AQS实现了获取锁失败后让线程加载到队列中阻塞等待,释放锁后唤醒同步队列中的线程等。而如何去获取锁由子类去实现,即由ReentrantLock内部类FairSync、NonfairSync来实现。此为典型的模板模式。
非公平锁的尝试抢锁代码:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {//如果临界状态state=0,非公平锁不管有没有线程在同步队列等待,先尝试用CAS跟新state状态
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//如果占用线程是当前线程,获取锁成功,跟新state值
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
1.通过ReentrantLock的加锁方法Lock进行加锁操作,会调用到内部类Sync的Lock方法
2.非公平锁会尝试用CAS去抢占临界状态获取锁。如果抢占成功则获取锁成功,抢占失败,则调用AQS#acquire方法获取锁。而公平锁则不会尝试 用CAS去抢占临界状态,此为公平锁与非公平锁的区别一。
3.AQS#acquire会先调用tryAcquire方法。判断临界状态state是否为0。若state非0,则判断当前当前线程是否已经持有锁,
currentThread = getExclusiveOwnerThread。若已经持有锁,根据锁的可重入性,获取锁成功,并且跟新state状态(state+1);若当前线程不持有锁,则获取锁失败
4.若临界状态state=0。非公平锁会直接尝试用CAS去抢占锁,而公平锁要先判断队列中没有其他线程在等待(!hasQueuedPredecessors)才会去抢占锁。此为公平锁与非公平锁的区别二。
尝试释放锁代码:
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(head) 把队列头的线程唤醒,这也体现了之前维护等待队列的一个重要作用。
1.通过ReentrantLock的释放锁方法unlock进行解锁操作,会调用到AQS的release方法
2.release会调用子类Sync#tryRelease方法释放锁,此时会先判断该线程持有锁,否则会抛出IllegalMonitorStateException异常。
3.如果state-release=0,则释放锁成功,把exclusiveOwnerThread设置为null,并且调用unparkSuccessor把等待队列中的头节点唤醒;
如果state-release!=0,则该锁为重入锁,释放锁失败。