Java并发编程-AbstractQueuedSynchronizer源码分析(8)
- UID
- 1066743
|
Java并发编程-AbstractQueuedSynchronizer源码分析(8)
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException该方法提供了具备有超时功能的获取状态的调用,如果在指定的nanosTimeout内没有获取到状态,那么返回false,反之返回true。可以将该方法看做acquireInterruptibly的升级版,也就是在判断是否被中断的基础上增加了超时控制。
针对超时控制这部分的实现,主要需要计算出睡眠的delta,也就是间隔值。间隔可以表示为nanosTimeout = 原有nanosTimeout – now(当前时间)+ lastTime(睡眠之前记录的时间)。如果nanosTimeout大于0,那么还需要使当前线程睡眠,反之则返回false。
[url=][/url]
01 private boolean doAcquireNanos(int arg, long nanosTimeout)02 throws InterruptedException {03 long lastTime = System.nanoTime();04 final Node node = addWaiter(Node.EXCLUSIVE);05 boolean failed = true;06 try {07 for (;;) {08 final Node p = node.predecessor();09 if (p == head && tryAcquire(arg)) {10 setHead(node);11 p.next = null; // help GC12 failed = false;13 return true;14 }15 if (nanosTimeout <= 0) return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold)16 LockSupport.parkNanos(this, nanosTimeout);17 long now = System.nanoTime();18 //计算时间,当前时间减去睡眠之前的时间得到睡眠的时间,然后被19 //原有超时时间减去,得到了还应该睡眠的时间20 nanosTimeout -= now - lastTime;21 lastTime = now;22 if (Thread.interrupted())23 throw new InterruptedException();24 }25 } finally {26 if (failed)27 cancelAcquire(node);28 }29 }[url=][/url]
上述逻辑主要包括:
1. 加入sync队列;
将当前线程构造成为节点Node加入到sync队列中。
2. 条件满足直接返回;
退出条件判断,如果前驱节点是头结点并且成功获取到状态,那么设置自己为头结点并退出,返回true,也就是在指定的nanosTimeout之前获取了锁。
3. 获取状态失败休眠一段时间;
通过LockSupport.unpark来指定当前线程休眠一段时间。
4. 计算再次休眠的时间;
唤醒后的线程,计算仍需要休眠的时间,该时间表示为nanosTimeout = 原有nanosTimeout – now(当前时间)+ lastTime(睡眠之前记录的时间)。其中now – lastTime表示这次睡眠所持续的时间。
5. 休眠时间的判定。
唤醒后的线程,计算仍需要休眠的时间,并无阻塞的尝试再获取状态,如果失败后查看其nanosTimeout是否大于0,如果小于0,那么返回完全超时,没有获取到锁。 如果nanosTimeout小于等于1000L纳秒,则进入快速的自旋过程。那么快速自旋会造成处理器资源紧张吗?结果是不会,经过测算,开销看起来很小,几乎微乎其微。Doug Lea应该测算了在线程调度器上的切换造成的额外开销,因此在短时1000纳秒内就让当前线程进入快速自旋状态,如果这时再休眠相反会让nanosTimeout的获取时间变得更加不精确。
上述过程可以如下图所示:
上述这个图中可以理解为在类似获取状态需要排队的基础上增加了一个超时控制的逻辑。每次超时的时间就是当前超时剩余的时间减去睡眠的时间,而在这个超时时间的基础上进行了判断,如果大于0那么继续睡眠(等待),可以看出这个超时版本的获取状态只是一个近似超时的获取状态,因此任何含有超时的调用基本结果就是近似于给定超时。 |
|
|
|
|
|