AQS是java locks 包下的一个基础包,实现了serializable 接口 继承了 AbstractOwnableSynchronizer 。其 重要的类属性有state 代表其状态,Node head tail 代表队列,还有AbstractOwnableSynchronizer 的exclusiveOwnerThread。
AQS 字段:state 0代表无线程持有该锁 state >0 代表有线程持有该锁
Node类型的head tail 来代表AQS的队列
Node中有next pre waitStatus Thread waitStatus -1 代表需要叫醒后继结点 0是默认值 1代表是cancelled。
ReentrantLock的 lock的方法底层就是使用了aqs ,比如Thread 1 和Thread2 同时想要竞争一把锁,即同时想执行 lock方法,则结果是只能有一个方法进入代码块,那么底层AQS是怎么操作的呢?首先lock的时候会通过cas 把state 从0 设置成1 ,我们知道只有一个线程会设置成功,从而将exclusiveOwnerThread 设置为该线程。之后的线程就会设置失败,不过之后仍然会尝试再次设置,acquire(1).
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire 就是使用nonFairTryAcquire(int arg) 来尝试获取锁的。cas 设置state 从0到1,设置成功则将此线程设置为拥有锁的独占线程,失败则判断是否是已经拥有了该锁,若已经拥有了该锁,则需要将state设置为state+1,否则就是尝试获取锁失败了。
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;
}
之后会acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
addWaiter() 是将尝试失败的线程封装成为Node 结点 加入等待队列,即插入双向列表中.具体逻辑是首先将该线程封装成Node结点,然后cas设置tail 为 Node,成功之后则返回封装好的Node ,失败则循环进行尾结点的更新。
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)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
如果cas失败 或者tail 为 空 则进行循环 设置
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueue 是将加入队列的结点 进行判断 ,如果其前一个结点是头结点代表当前或者之前的线程获得过锁,那么此时可以尝试获取锁,如果不是则需要进行阻塞等待,阻塞等待也是有说法的比如你不能把新结点Node 放入任意结点的后一结点,比如前一结点的waitstatus
是 0 或者大于0 都需要进行改变前一个结点的状态或者直接删除前一个结点 直到前一个结点waitstatus
小于0.这样就可以进行有效的唤醒该线程。这就是shouldParkAfterFailedAcquire 的目的,之后便可以进行park。值得注意的是在线程park后 不能响应中断,苏醒之后会通过Thread.interrupted() 返回之前是否被其他线程中断并且会重置中断标志。之后在进行自我中断。注意Thread.isInterrupted() 函数与Thread.interrupted()函数的异同。isInterrupted 只是判断该线程是否被其他线程中断过,不会重新设置中断标志。
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);
}
}
自定义序列同步器应该主要实现一下方法:
isHeldExclusively() 是否持正在独占资源。只有在使用Condition 时候才需要去实现它
tryAcquire(int arg) 尝试获取锁 成功 true 失败返回false
tryRelease(int arg) 尝试释放锁 成功 true 失败返回false
tryAcquireShared(int) 尝试获取锁 负数 表示失败 0 成功 但是无剩余资源 整数代表成功。
tryRealeaseShared(int) 尝试释放锁 如果释放后允许唤醒后序等待结点 返回true 否则返回false。