AQS 独占式同步状态获取与释放
AQS的 acquire 方法
通过调用同步器的acquire(int arg)方法可以获取同步状态, 该方法对中断不敏感, 也就是 由于线程获 取同步状态失败后进入同步队列中, 后续对线程进行中断操作时, 线程不会从同 步队列中移出
上述代码主要完成了同步状态获取 、 节点构造 、 加入同步队列以及在同步队列中自旋等 待的相关工 作, 其主要逻辑是: 首先调用自定义同步器实现的tryAcquire(int arg)方法, 该方法 保证线程安全的 获取同步状态, 如果同步状态获取失败, 则构造同步节点 (独占式 Node.EXCLUSIVE, 同一时刻只能 有一个线程成功获取同步状态) 并通过addWaiter(Node node) 方法将该节点加入到同步队列的尾部, 最后调用acquireQueued(Node node,int arg)方法, 使得该节点以“死循环”的方式获取同步状态 。如果 获取不到则阻塞节点中的线程, 而被阻塞线程的 唤醒主要依靠前驱节点的出队或阻塞线程被中断来 实现。
上述代码通过使用compareAndSetTail(Node expect,Node update)方法来确保节点能够被线 程安全添加。
在enq(final Node node)方法中, 同步器通过“死循环”来保 证节点的正确添加, 在“死循 环”中只有通过CAS将节点设 置成为尾节点之后, 当前线程才能从该方法返回, 否则 , 当前线 程不断地尝试设置 。 可以看出, enq(final Node node)方法将并发添加节点的请求通过CAS变 得“串行化”了。
如何前驱节点是头节点, 则尝试获取 同步锁 。 而只有前驱节点是头节点才
能够尝试获取同步状态, 这是为什么? 头节点是成功获取到同步状态的节点 , 而头节点的线程释放了同步状态之后 , 将会 唤醒其后继节点, 后继节点的 线程被唤醒后需要检查自己的前驱节点 是否是头节点。
如果前驱节点不是头节点, 则获取同步锁失败, 那么线程 继续在同步队列中等待
独占式同步状态获取流程, 也就是acquire(int arg)方法调用流程如下所示:
当前线程获取同步状态并执行了相应逻辑之后, 就需要释放同步状态, 使得后续节点能 够继 续获取同步状态 。 通过调用同步器的release(int arg)方法可以释放同步状态, 该方法在释 放了 同步状态之后, 会唤醒其后继节点 (进而使后继节点重新尝试获取同步状态) 。该方法代 码 如下所示:
该方法执行时, 会唤醒头节点的后继节点线程, unparkSuccessor(Node node)方法使用 LockSupport (在后面的有关LockSupport题目来专门说明) 来唤醒处于等待状态的线程。
|