在CLH锁核心思想的影响下,JDK并发包以CLH锁做为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能。比起原来的CLH锁已经作了很大的改造,主要从两方面进行了改造:节点的结构与节点等待机制。javascript
在结构上引入了头结点和尾节点,他们分别指向队列的头和尾,尝试获取锁、入队列、释放锁等实现都与头尾节点相关,而且每一个节点都引入前驱节点和后后续节点的引用;在等待机制上由原来的自旋改为阻塞唤醒。如图,经过前驱后续节点的引用一节节链接起来造成一个链表队列,对于头尾节点的更新必须是原子的。下面详细看看入队、检测挂起、释放出队、超时、取消等操做。html
整块逻辑实际上是用一个无限循环进行CAS操做,即用自旋方式竞争直到成功。将尾节点tail的旧值赋予新节点node的前驱节点,并尝试CAS操做将新节点node赋予尾节点tail,原先的尾节点的后续节点指向新建节点node。完成上面步骤就创建起一条如图所示的链表队列。代码简化以下:java
for (;;) {
Node t = tail;
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return node;
}
}复制代码
上面咱们说到节点等待机制已经被JDK并发做者由自旋机制改形成阻塞机制,一个新建的节点完成入队操做后,若是是自旋则直接进入循环检测前驱节点是否为头结点便可,但如今被改成阻塞机制,当前线程将首先检测是否为头结点且尝试获取锁,若是当前节点为头结点并成功获取锁则直接返回,当前线程不进入阻塞,不然将当前线程阻塞。代码简化以下:node
for (;;) {
if (node.prev == head)
if(尝试获取锁成功){
head=node;
node.next=null;
return;
}
阻塞线程
}复制代码
出队的主要工做是负责唤醒等待队列中后续节点,让全部等待节点环环相接,每条线程有序地往下执行。代码简化以下:并发
Node s = node.next;
唤醒节点s包含的线程复制代码
在支持超时的模式下须要LockSupport类的parkNanos方法支持,线程在阻塞一段时间后会自动唤醒,每次循环将累加消耗时间,当总消耗时间大于等于自定义的超时时间时就直接分返。代码简化以下:this
for (;;) {
尝试获取锁
if (nanosTimeout <= 总消耗时间)
return;
LockSupport.parkNanos(this, nanosTimeout);
}复制代码
队列中等待锁的队列可能由于中断或超时而涉及到取消操做,这种状况下被取消的节点再也不进行锁竞争。此过程主要完成的工做是将取消的节点移除,先将节点的。先将节点node状态设置成取消,再将前驱节点pred的后续节点指向node的后续节点,这里因为涉及到竞争,必须经过CAS进行操做,CAS操做就算失败也没必要理会,由于已经改了节点的状态,在尝试获取锁操做中会循环对节点的状态判断。spa
node.waitStatus = Node.CANCELLED;
Node pred = node.prev;
Node predNext = pred.next;
Node next = node.next;
compareAndSetNext(pred, predNext, next);复制代码
====广告时间,可直接跳过====线程
鄙人的新书《Tomcat内核设计剖析》已经在京东预售了,有须要的朋友能够到 item.jd.com/12185360.ht… 进行预约。感谢各位朋友。设计
=========================code
欢迎关注: