java Lock的实现AbstractQueuedSynchronizer源码阅读(一)ConditionObject解析_java.util.concurrent.locks.abstractqueuedsynchroni-程序员宅基地

技术标签: 笔记  java锁的实现  ConditionObject  

一、Condition接口

public interface Condition {
     /** 
      * 暂停此线程直至一下四种情况发生
      * 1.此Condition被signal()
      * 2.此Condition被signalAll()
      * 3.Thread.interrupt()
      * 4.伪wakeup
      * 以上情况.在能恢复方法执行时,当前线程必须要能获得锁
      */
    void await() throws InterruptedException;
    //跟上面类似,不过不响应中断
    void awaitUninterruptibly();
    //带超时时间的await()
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    //带超时时间的await()
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    //带deadline的await()
    boolean awaitUntil(Date deadline) throws InterruptedException;
    //唤醒某个等待在此condition的线程
    void signal();
    //唤醒所有等待在此condition的所有线程
    void signalAll();
}

二、ConditionObject解析

/**
 * ConditionObject的实现,其实其维护两个队列
 * 1)Condition队列,表示等待的队列,其waitStatus=Node.CONDITION,由firstWaiter和lastWaiter两个属性操控. 
 * 2)Sync队列,表示可以竞争锁的队列,这个跟AQS一致,waitStatus=0;
 * 3)await()把当前线程创建一个Node加入Condition队列,接着就一直循环查其在不在Sync队列
 *   如果当前节点在Sync队列里了,就可以竞争锁,恢复运行了.
 * 4)signal()方法就是把某个节点的nextWaiter设为null,再把其从Condition队列转到Sync队列
 */
public class ConditionObject implements Condition, java.io.Serializable {
	//condition队列的第一个节点
	private transient Node firstWaiter;
	//condition队列的最后一个节点
	private transient Node lastWaiter;
	public ConditionObject() { }
	/**
	 * 往condition队列中添加新元素
	 */
	private Node addConditionWaiter() {
		Node t = lastWaiter;
		// If lastWaiter is cancelled, clean out.
		if (t != null && t.waitStatus != Node.CONDITION) {
			unlinkCancelledWaiters();//遍历condition队列,移除其中非CONDITION状态的元素
			//lastWaiter非CONDITION状态,上一步遍历清除时lastWaiter发生了变化需要重新赋值
			t = lastWaiter;
		}
		//新建一个CONDITION状态的节点,并将其加在condition队列尾部
		Node node = new Node(Node.CONDITION);
		if (t == null)//lastWaiter为null
			firstWaiter = node;//firstWaiter指向新的node
		else
			t.nextWaiter = node;//lastWaiter不为null,node拼在lastWaiter后面
		lastWaiter = node;//lastWaiter指向新增加在末尾的元素
		return node;//返回新增元素
	}

	/**
	 * 暂停此线程,直至进入SyncQueue
	 * 线程唤醒时如果还是不在SyncQueue,会继续park
	 */
	public final void awaitUninterruptibly() {
		Node node = addConditionWaiter();//往condition队列中添加新元素
		int savedState = fullyRelease(node);//释放当前线程的锁
		boolean interrupted = false;
		while (!isOnSyncQueue(node)) {//node节点不在SyncQueue,就一直循环
			LockSupport.park(this);//阻塞node的线程
			//线程唤醒时,继续循环,如果还是不在SyncQueue,继续park休眠
			if (Thread.interrupted())
				interrupted = true;
		}
		//跳出来,说明线程进入SyncQueue了,acquireQueued作用:阻塞node
		if (acquireQueued(node, savedState) || interrupted)
			selfInterrupt();//中断线程
	}
	
	/**
	 * 释放信号
	 */
	public final void signal() {
		if (!isHeldExclusively())//不是独占模式(排它模式)抛出异常
			throw new IllegalMonitorStateException();
		//将等待队列的第一个节点出队列,并将其加入AQS的锁队列
		Node first = firstWaiter;
		if (first != null)
			doSignal(first);
	}
	
	/**
	 * Removes and transfers nodes until hit non-cancelled one or
	 * null. Split out from signal in part to encourage compilers
	 * to inline the case of no waiters.
	 * @param first (non-null) the first node on condition queue
	 */
	private void doSignal(Node first) {
		do {
			if ((firstWaiter = first.nextWaiter) == null)//第一个元素空了,说明condition队列空了
				lastWaiter = null;//最后一个元素引用置空
			//将first.nextWaiter置空,也就是从condition队列中出列
			first.nextWaiter = null;
		//如果转换队列不成功且condition队列不为null,继续循环
		} while (!transferForSignal(first) && (first = firstWaiter) != null);
	}

	/**
	 * 唤醒所有等待在此condition的所有线程
	 */
	public final void signalAll() {
		if (!isHeldExclusively())//非独占模式,抛出异常
			throw new IllegalMonitorStateException();
		Node first = firstWaiter;
		if (first != null)
			doSignalAll(first);
	}
	
	/**
	 * 唤醒所有等待在此condition的所有线程
	 */
	private void doSignalAll(Node first) {
		lastWaiter = firstWaiter = null;
		do {//循环遍历condition队列,一个个塞到Sync队列中
			Node next = first.nextWaiter;
			first.nextWaiter = null;
			transferForSignal(first);
			first = next;
		} while (first != null);
	}

	/**
	 * 遍历condition队列,移除其中非CONDITION状态的元素
	 */
	private void unlinkCancelledWaiters() {
		Node t = firstWaiter;
		Node trail = null;
		while (t != null) {//遍历condition队列
			Node next = t.nextWaiter;
			if (t.waitStatus != Node.CONDITION) {
				t.nextWaiter = null;//这样t就从condition中移除了
				if (trail == null)
					firstWaiter = next;//指向下一个元素,再次遍历
				else//trail是上一个元素且为CONDITION状态
					trail.nextWaiter = next;//上一个元素与本元素的下一个元素相连,本元素被剔除
				if (next == null)
					lastWaiter = trail;//没有下一个元素了,lastWaiter指向trail
			} else {
				trail = t;
			}
			t = next;
		}
	}
	...
	/**
	 * 参考上面对awaitUninterruptibly(..)的分析
	 */
	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);
	}

	/**
	 * 参考上面对awaitUninterruptibly(..)的分析
	 */
	public final long awaitNanos(long nanosTimeout) throws InterruptedException {
		if (Thread.interrupted())
			throw new InterruptedException();
		// We don't check for nanosTimeout <= 0L here, to allow
		// awaitNanos(0) as a way to "yield the lock".
		final long deadline = System.nanoTime() + nanosTimeout;
		long initialNanos = nanosTimeout;
		Node node = addConditionWaiter();
		int savedState = fullyRelease(node);
		int interruptMode = 0;
		while (!isOnSyncQueue(node)) {
			if (nanosTimeout <= 0L) {
				transferAfterCancelledWait(node);
				break;
			}
			if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
				LockSupport.parkNanos(this, nanosTimeout);
			if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
				break;
			nanosTimeout = deadline - System.nanoTime();
		}
		if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
			interruptMode = REINTERRUPT;
		if (node.nextWaiter != null)
			unlinkCancelledWaiters();
		if (interruptMode != 0)
			reportInterruptAfterWait(interruptMode);
		long remaining = deadline - System.nanoTime(); // avoid overflow
		return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE;
	}

	/**
	 * 参考上面对awaitUninterruptibly(..)的分析
	 */
	public final boolean awaitUntil(Date deadline) throws InterruptedException {
		long abstime = deadline.getTime();
		if (Thread.interrupted())
			throw new InterruptedException();
		Node node = addConditionWaiter();
		int savedState = fullyRelease(node);
		boolean timedout = false;
		int interruptMode = 0;
		while (!isOnSyncQueue(node)) {
			if (System.currentTimeMillis() >= abstime) {
				timedout = transferAfterCancelledWait(node);
				break;
			}
			LockSupport.parkUntil(this, abstime);
			if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
				break;
		}
		if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
			interruptMode = REINTERRUPT;
		if (node.nextWaiter != null)
			unlinkCancelledWaiters();
		if (interruptMode != 0)
			reportInterruptAfterWait(interruptMode);
		return !timedout;
	}

	/**
	 * 参考上面对awaitUninterruptibly(..)的分析
	 */
	public final boolean await(long time, TimeUnit unit)
			throws InterruptedException {
		long nanosTimeout = unit.toNanos(time);
		if (Thread.interrupted())
			throw new InterruptedException();
		// We don't check for nanosTimeout <= 0L here, to allow
		// await(0, unit) as a way to "yield the lock".
		final long deadline = System.nanoTime() + nanosTimeout;
		Node node = addConditionWaiter();
		int savedState = fullyRelease(node);
		boolean timedout = false;
		int interruptMode = 0;
		while (!isOnSyncQueue(node)) {
			if (nanosTimeout <= 0L) {
				timedout = transferAfterCancelledWait(node);
				break;
			}
			if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
				LockSupport.parkNanos(this, nanosTimeout);
			if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
				break;
			nanosTimeout = deadline - System.nanoTime();
		}
		if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
			interruptMode = REINTERRUPT;
		if (node.nextWaiter != null)
			unlinkCancelledWaiters();
		if (interruptMode != 0)
			reportInterruptAfterWait(interruptMode);
		return !timedout;
	}
	...
	/**
	 * 获取condition队列中所有等待的线程并返回
	 */
	protected final Collection<Thread> getWaitingThreads() {
		if (!isHeldExclusively())
			throw new IllegalMonitorStateException();
		ArrayList<Thread> list = new ArrayList<>();
		for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
			if (w.waitStatus == Node.CONDITION) {
				Thread t = w.thread;
				if (t != null)
					list.add(t);
			}
		}
		return list;
	}
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010577768/article/details/79943628

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签