介绍了AQS内置队列节点的出队入队操作,以及独占式获取共享资源与释放资源的详细流程,为了结构完整,本篇继续以AQS的角度介绍另外一种:共享模式获取与释放资源的细节,本篇暂不分析具体子类如ReentrantLock、ReentrantReadWriteLock的实现,之后会陆续补充。
独占式获取资源
友情提示:本篇文章着重介绍共享模式获取和释放资源的特点,许多代码实现上面和共享式和独占式其实逻辑差不多,为了清晰对比,这边会将独占式的部分核心代码粘贴过来,注意理解共享式和独占式存在差异的地方。详细解析可看我上一篇:Java并发包源码学习系列:CLH同步队列及同步资源获取与释放
void acquire(int arg)
public final void acquire(int arg) { if (!tryAcquire(arg) && // tryAcquire由子类实现,表示获取锁,如果成功,这个方法直接返回了 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果获取失败,执行 selfInterrupt(); }
boolean acquireQueued(Node, int)
// 这个方法如果返回true,代码将进入selfInterrupt() final boolean acquireQueued(final Node node, int arg) { // 注意默认为true boolean failed = true; try { // 是否中断 boolean interrupted = false; // 自旋,即死循环 for (;;) { // 得到node的前驱节点 final Node p = node.predecessor(); // 我们知道head是虚拟的头节点,p==head表示如果node为阻塞队列的第一个真实节点 // 就执行tryAcquire逻辑,这里tryAcquire也需要由子类实现 if (p == head && tryAcquire(arg)) { // tryAcquire获取成功走到这,执行setHead出队操作 setHead(node); p.next = null; // help GC failed = false; return interrupted; } // 走到这有两种情况 1.node不是第一个节点 2.tryAcquire争夺锁失败了 // 这里就判断 如果当前线程争锁失败,是否需要挂起当前这个线程 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { // 死循环退出,只有tryAcquire获取锁失败的时候failed才为true if (failed) cancelAcquire(node); } }
独占式释放资源
boolean release(int arg)
public final boolean release(int arg) { if (tryRelease(arg)) { // 子类实现tryRelease方法 // 获得当前head Node h = head; // head不为null并且head的等待状态