目录
AQS(AbstractQueuedSynchronizer)它是java一个同步工具类,在我们很多常见的类中都有用到他,基于他实现,比如ReentrantLock、ReentrantReadWriteLock、FutureTask等。
原理
底层用了一个整数类型变量去表示同步状态,并通过CAS管理同步状态对象。
流程大概类似与这样
节点是双向链表,便于移除和添加。
这样看起来是不是直观很多。
原理源码解读
AQS可以实现独占锁和共享锁,我们以独占锁为例去看
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先看加锁tryAcquire的方法
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
发现什么也没有,那必然他的子类重写这个方法,我们以ReentrantLock的NonfairSync为例去看一下。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
//拿到当前线程
final Thread current = Thread.currentThread();
//获取state
int c = getState();
//如果为0,认为可以用CAS抢占
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;
}
总的来说 就是获取state,成功返回true,失败则false。
然后是acquireQueued方法
先看addWaiter方法
private Node addWaiter(Node mode) {
//把当前线程和mode塞入新创建的Node节点
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)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
入参为锁模式(共享还是独占),我们发现其中有个变量tail,看一下它的注释,大概意思是队列的尾部,懒初始化,仅通过enq方法去添加新的等待节点
/** * Tail of the wait queue, lazily initialized. Modified only via * method enq to add new wait node. */ private transient volatile Node tail;
所以它的大致流程就是判断是否为新的队列,如果不是就往后边加,如果是就新建。
acquireQueued方法
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())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
首先看自己的前直节点是不是头节点,是的话,尝试获取资源成功就把自己设为头节点。失败的话就继续阻塞
解锁release源码
public final boolean release(int arg) {
//尝试释放锁,子类实现
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//释放锁锁成功后唤醒最合适的线程
unparkSuccessor(h);
return true;
}
return false;
}
有什么建议或意见请及时提出