java模板模式与AQS实现( 二 )


成功后各个线程调用方法进入自旋状态:
final boolean acquireQueued(final Node node, int arg) {boolean interrupted = false;try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCreturn interrupted;}if (shouldParkAfterFailedAcquire(p, node))interrupted |= parkAndCheckInterrupt();}} catch (Throwable t) {cancelAcquire(node);if (interrupted)selfInterrupt();throw t;}}
这里需要注意两点,一是只有当前节点的前驱为头节点时才可尝试获取资源(),保证队列的先进先出嘛,
二是一个细节,p.next = null; 这个操作使得p的后继标记为空,帮助GC完成垃圾收集 。对象标记为null来促进GC在大部分情况下是错误的!因为编译器会对这个操作进行优化,赋值为null其实大部分情况下是被干掉的,没有意义,但这里是正确的,后续可能更新博客详细解释 。
至此,一个线程可以顺利获取资源,如果获取不到则添加到同步队列里,一旦自旋轮到自己就可以获取到资源 。可是还没释放呢,总不能自己占着不然别人用吧 。下边是释放资源相关逻辑:
public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;}
同样是子类必须复写的方法,否则直接抛异常 。释放资源(修改同步状态state)成功后,会执行下边的核心方法:
private void unparkSuccessor(Node node) {……………其他逻辑……………/** Thread to unpark is held in successor, which is normally* just the next node.But if cancelled or apparently null,* traverse backwards from tail to find the actual* non-cancelled successor.*/Node s = node.next;if (s == null || s.waitStatus > 0) {s = null;for (Node p = tail; p != node && p != null; p = p.prev)if (p.waitStatus <= 0)s = p;}if (s != null)LockSupport.unpark(s.thread);}
大部分情况会执行.(s.);注释也说道如果s为空了,则从后向前遍历得到一个非空的继任者,但最终都是调用.(s.);这个方法作用是唤醒某个线程,在这个情景下就是唤醒下一个等待的线程 。注意这里后没有直接删除相应节点,删除节点在上述方法中 。
至此一个独占式锁完成 。大体流程如下:
情况二:共享式资源
共享式与独占式核心区别在于,前者可以同时有多个线程访问同步状态state,后者仅能有一个线程访问,与独占锁类似,AQS提供如下模板:
public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0)doAcquireShared(arg);}
为子类复写,注意这里不同的是返回数值小于0后才执行,意为,共享资源可以有多个线程同时访问资源 。
private void doAcquireShared(int arg) {final Node node = addWaiter(Node.SHARED);boolean interrupted = false;try {for (;;) {final Node p = node.predecessor();if (p == head) {int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCreturn;}}if (shouldParkAfterFailedAcquire(p, node))interrupted |= parkAndCheckInterrupt();}} catch (Throwable t) {cancelAcquire(node);throw t;} finally {if (interrupted)selfInterrupt();}}
与独占类似,也是添加节点,然后进入自旋,如果前驱是头节点则尝试获取资源,不在赘述 。
释放资源最终也是调用方法,与独占锁一致 。
情况三:独占超时
与独占模型一样,只不过加上超时控制:
private boolean doAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {if (nanosTimeout