共计 1627 个字符,预计需要花费 5 分钟才能阅读完成。
摘要排队同步器类
它提供了一个框架,用于实现阻塞锁和相关的同步器,如信号量,CountDownLatch 等。获取的基本算法是 try acquire,如果成功则返回其他排队线程(如果它尚未排队)并阻止当前线程。同样,发布的基本算法是 try release,如果成功,则取消阻塞队列中的第一个线程,否则只返回。线程将在先进先出(FIFO)等待队列中等待。抽象方法 tryAcquire()和 tryRelease()将根据需要由子类实现。
以独家模式获取
以独占模式获取的通用算法
AbstractQueuedSynchronizer(AQS)
在上面的图中 ’shouldParkAfterFailedAcquire?’ 验证前任的等待状态是否为 SIGNAL。如果是,它确定前任线程将在其释放时 SIGNAL,因此它将立即阻止,否则它可能会重试获取锁,以防它是队列中的第一个节点。
AbstractQueuedSynchronizer(AQS)
队列
如果线程无法获取锁,它将被放入队列中。如果队列尚不存在,它将使用虚拟标头初始化它,然后将其自身链接到它。头部的“下一个”和节点的“上一个”将被链接。新节点也成了尾巴。标题节点的等待状态将设置为 SIGNAL,以便当所有者线程释放锁时,它可以通知头节点的后继者获取锁。线程将再次尝试获取锁定以确保它在实际停放之前无法获取。
AbstractQueuedSynchronizer(AQS)
因此,只要其前任节点的等待状态被设置为 SIGNAL,就可以安全地停放未能获得锁的线程,因此一旦前一个被释放,它就可以重试获取锁。
如果前一个被取消,它将跳过所有被取消的前任,以重置其等待线程的 next 和 prev 指针。
AbstractQueuedSynchronizer(AQS)
发布
AbstractQueuedSynchronizer(AQS)
子类将根据他们的要求实现“try Release”。一旦发布,标头节点的后继节点需要发信号,以便它可以重新尝试获取。如果没有头,则表示队列中没有线程,因此没有人发出信号。如果磁头存在,则确保等待状态不为零。如果它为零,则意味着不需要发信号通知后继节点。
Unpark 后继节点的线程
线程到 unpark 是在后继节点,通常只是下一个节点。
情况 1:如果头部的等待状态 <0,则清除等待状态。如果后继节点(P1)未处于取消状态,则取消后继节点的线程,以便它可以重试获取。
AbstractQueuedSynchronizer(AQS)
情况 2:如果后继节点取消或为 null,则从尾部向后遍历以查找实际未取消的后继节点。
AbstractQueuedSynchronizer(AQS)
一旦取消停放线程,其节点就成了新头。老头将脱钩。如果未能获得,将重新停放。头节点的等待状态设置为 0 将重置为 SIGNAL。
发布共享
这与独家发布类似。它还确保释放传播。
以共享模式获取
这类似于独家收购。它还将释放传播到队列中等待获取共享锁的其他等待线程。一旦锁定被释放,它就会取消其后继节点的停放,后者又将释放传播到下一个节点。
取消
在尝试获取时可能存在运行时异常,在这种情况下将取消上下文中的节点。如果节点被取消,我们必须确保其后继节点正确链接到有效的前任节点,因此可能必须调整链接。如果其前任节点已经处于取消状态,则将跳过这些节点以到达具有等待状态 <= 0 的适当的前任节点。
如果要取消的节点本身是尾节点,则将简单地将其移除。它的前身节点将成为新的尾巴。新尾部的“下一个”链接将指向 null。
如果等待状态 <0,则表示后继者需要信号,尝试设置前任的下一个链接,以便获得一个。如果前任是头节点本身,则它将唤醒其后继节点。
情况 1:要取消的节点是尾节点
AbstractQueuedSynchronizer(AQS)
情况 2:取消节点的前任是 head,现在将发信号通知被取消节点的下一个节点被唤醒。
原文地址:https://www.javarticles.com/2…