1.5w字,30图带你彻底掌握 AQS!(建议收藏)( 八 )


所以当 T4 执行这段代码时 , 会变成如下情况
1.5w字,30图带你彻底掌握 AQS!(建议收藏)文章插图
可以看到此时中间的两个 CANCEL 节点不可达了 , 会被 GC
3、如果当前结点为 tail 结点 , 则结果如下 , 这种情况下当前结点不可达 , 会被 GC
1.5w字,30图带你彻底掌握 AQS!(建议收藏)文章插图
4、如果当前结点为 head 的后继结点时 , 如下
1.5w字,30图带你彻底掌握 AQS!(建议收藏)文章插图
结果中的 CANCEL 结点同样会在 tail 结点自旋调用 shouldParkAfterFailedAcquire 后不可达 , 如下
1.5w字,30图带你彻底掌握 AQS!(建议收藏)文章插图
自此我们终于分析完了锁的获取流程 , 接下来我们来看看锁是如何释放的 。
锁释放不管是公平锁还是非公平锁 , 最终都是调的 AQS 的如下模板方法来释放锁
// java.util.concurrent.locks.AbstractQueuedSynchronizerpublic final boolean release(int arg) {// 锁释放是否成功if (tryRelease(arg)) {Node h = head;if (h != nullreturn true;}return false;}tryRelease 方法定义在了 AQS 的子类 Sync 方法里
// java.util.concurrent.locks.ReentrantLock.Syncprotected final boolean tryRelease(int releases) {int c = getState() - releases;// 只有持有锁的线程才能释放锁 , 所以如果当前锁不是持有锁的线程 , 则抛异常if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;// 说明线程持有的锁全部释放了 , 需要释放 exclusiveOwnerThread 的持有线程if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}锁释放成功后该干嘛 , 显然是唤醒之后 head 之后节点 , 让它来竞争锁
// java.util.concurrent.locks.AbstractQueuedSynchronizerpublic final boolean release(int arg) {// 锁释放是否成功if (tryRelease(arg)) {Node h = head;if (h != nullreturn true;}return false;}这里释放锁的条件为啥是 h != nullif (ws < 0)compareAndSetWaitStatus(node, ws, 0);// 以下操作为获取队列第一个非取消状态的结点 , 并将其唤醒Node s = node.next;// s 状态为非空 , 或者其为取消状态 , 说明 s 是无效节点 , 此时需要执行 if 里的逻辑if (s == null || s.waitStatus > 0) {s = null;// 以下操作为从尾向前获取最后一个非取消状态的结点for (Node t = tail; t != nullt = t.prev)if (t.waitStatus <= 0)s = t;}if (s != null)LockSupport.unpark(s.thread);}这里的寻找队列的第一个非取消状态的节点为啥要从后往前找呢 , 因为节点入队并不是原子操作 , 如下
1.5w字,30图带你彻底掌握 AQS!(建议收藏)文章插图
线程自旋时时是先执行 node.pre = pred, 然后再执行 pred.next = node , 如果 unparkSuccessor 刚好在这两者之间执行 , 此时是找不到 head 的后继节点的 , 如下
1.5w字,30图带你彻底掌握 AQS!(建议收藏)文章插图
如何利用 AQS 自定义一个互斥锁AQS 通过提供 state 及 FIFO 队列的管理 , 为我们提供了一套通用的实现锁的底层方法 , 基本上定义一个锁 , 都是转为在其内部定义 AQS 的子类 , 调用 AQS 的底层方法来实现的 , 由于 AQS 在底层已经为了定义好了这些获取 state 及 FIFO 队列的管理工作 , 我们要实现一个锁就比较简单了 , 我们可以基于 AQS 来实现一个非可重入的互斥锁 , 如下所示