尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(二)
来源:互联网 发布:香港网络歌手小背心 编辑:程序博客网 时间:2024/05/26 12:58
尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码 (一)
这篇文章希望通过对 AbstractQueuedSynchronizer
内部类 ConditionObject
的探索,加深对阻塞唤醒机制的理解!
一、await()
方法流程
ReentrantLock lock = new ReentrantLock(true);Condition con = lock.newCondition();con.await();
这一段代码很常见,当调用await方法,阻塞当前线程。下面我们来看看具体实现:
AbstractQueuedSynchronizer.ConditionObject.await()public final void await() throws InterruptedException { //手动检测当前线程是否中断,并清空中断标识 //如果中断,则抛出中断异常 if (Thread.interrupted()) throw new InterruptedException(); //创建一个节点并添加到一个队列中 Node node = addConditionWaiter(); //释放该节点持有的锁 int savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) { LockSupport.park(this);//线程阻塞于此 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); }
加入condition队列
下面详细看下addConditionWaiter
AbstractQueuedSynchronizer.ConditionObject.addConditionWaiter()private Node addConditionWaiter() { Node t = lastWaiter; // If lastWaiter is cancelled, clean out. if (t != null && t.waitStatus != Node.CONDITION) { unlinkCancelledWaiters(); t = lastWaiter; } Node node = new Node(Thread.currentThread(), Node.CONDITION); if (t == null) firstWaiter = node; else t.nextWaiter = node; lastWaiter = node; return node; }
通过这段代码,其实不难发现:
1、每个condition
有一个关联的队列,并且这个队列是个单向链表;
2、每个condition
队列头节点为firstWaiter
、尾节点为lastWaiter
。使用nextWaiter
进行彼此之间的关联
到现在为止,我们知道了两个队列,同步队列(阻塞队列)和条件队列(
condition
队列),那么这两个队列之间有什么关联呢? 值得我们关注
unlinkCancelledWaiters
方法用于清除队列状态不为-2的节点。
private void unlinkCancelledWaiters() { Node t = firstWaiter; Node trail = null; while (t != null) { Node next = t.nextWaiter; if (t.waitStatus != Node.CONDITION) { t.nextWaiter = null; if (trail == null) firstWaiter = next; else trail.nextWaiter = next; if (next == null) lastWaiter = trail; } else trail = t; t = next; } }
释放线程持有的锁
线程被阻塞时,同时需要放弃自己所持有的锁,并唤醒后继节点。最后将节点状态改为-2。在释放锁时,如果当前线程不是锁持有者,则抛出IllegalMonitorStateException
异常。
final int fullyRelease(Node node) { boolean failed = true; try { int savedState = getState(); if (release(savedState)) { failed = false; return savedState; } else { throw new IllegalMonitorStateException(); } } finally { if (failed) //将当前节点状态修改为-2;当添加下一个节点时,该节点将会被清除 node.waitStatus = Node.CANCELLED; } }
等待进入同步队列
//判断当前线程是否在同步队列中final boolean isOnSyncQueue(Node node) { //如果节点状态为-2,或者无前驱节点,返回false if (node.waitStatus == Node.CONDITION || node.prev == null) return false; //如果next属性不为null,返回true if (node.next != null) // If has successor, it must be on queue return true; /* * node.prev can be non-null, but not yet on queue because * the CAS to place it on queue can fail. So we have to * traverse from tail to make sure it actually made it. It * will always be near the tail in calls to this method, and * unless the CAS failed (which is unlikely), it will be * there, so we hardly ever traverse much. */ // 从队尾遍历 return findNodeFromTail(node); }
线程不断自旋来确认是否还在同步队列中,如果不在同步队列中了,则阻塞当前线程;原来await
方法使用的就是park
。
二、signal()
方法流程
使用该用法用于唤醒一个阻塞的线程,该方法只有锁持有者可以调用,否则抛出异常。
public final void signal() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignal(first); }
private void doSignal(Node first) { do { //将firstWaiter指向第二个等待的节点 //如果第二个等待的节点为null,让尾节点修改为null if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; //去掉与第二个等待节点的管理 first.nextWaiter = null; }//如果转移失败,则循环转移下一个节点,直到成功 while (!transferForSignal(first) && (first = firstWaiter) != null); }
//将节点node转移到同步队列final boolean transferForSignal(Node node) { /* * If cannot change waitStatus, the node has been cancelled. * 如果无法修改node节点是waitStatus,说明node节点的状态为1。 * 进行条件队列下一个节点的转移 */ if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; //进入阻塞队列尾部,p表示前驱节点 Node p = enq(node); int ws = p.waitStatus; //如果前驱节点状态为1(已经取消),或者改变前驱节点的状态为-1失败,则直接唤醒node节点 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; }
阅读全文
0 0
- 尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(二)
- 尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(一)
- AbstractQueuedSynchronizer源码解析之ReentrantLock(二)
- 可重入锁 ReentrantLock 源码解读 (2)锁框架 AbstractQueuedSynchronizer
- AbstractQueuedSynchronizer详解(一)——分析ReentrantLock源码
- 源码阅读笔记:AbstractQueuedSynchronizer
- AbstractQueuedSynchronizer源码解析之ReentrantLock(一)
- JDK之ReentrantLock,AbstractQueuedSynchronizer源码分析
- 源码分析----ReentrantLock实现和AbstractQueuedSynchronizer
- ReentrantLock之AbstractQueuedSynchronizer 源码分析笔记
- AbstractQueuedSynchronizer实现源码解析(二)
- ReentrantLock源码阅读心得
- Lock、ReentrantLock和AbstractQueuedSynchronizer的源码要点分析整理
- Lock、ReentrantLock和AbstractQueuedSynchronizer的源码要点分析整理
- ReentrantLock源码分析(二)
- AbstractQueuedSynchronizer详解(二)——CountDownLatch源码分析
- AbstractQueuedSynchronizer源码分析二(共享锁部分)
- jdk-AbstractQueuedSynchronizer(二)
- 1004. 成绩排名 (20)
- SSM里关于mysql主从配置代码层实现
- springmvc
- JAVA设计模式之【代理模式】二(jdk动态代理)
- Leetcode:14. Longest Common Prefix
- 尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(二)
- RPC入门总结(五)RPC IO基础:Netty高性能并发关键技术点
- Week 1, Aerial Robotics
- Android心跳包(一)——心跳机制
- php socket IO阻塞方式的Server/Client
- 想去阿里巴巴面试么
- Nginx学习-初步理解
- leetcode 173. Binary Search Tree Iterator
- maven私服nexus忘记密码,解决方法(转载)