6.类似Object监视器方法的Condition接口
在《1.有关线程、并发的基本概念》中,我们利用synchronized关键字、Queue队列、以及Object监视器方法实现了生产者消费者,介绍了有关线程的一些基本概念。Object类提供的wait的方法和notifyAll方法,与之对应的是Condition接口提供是await和signalAll。await(或wait)是让当前线程进入等待状态并释放锁,signalAll(或notifyAll)则是唤醒等待中的线程,使得等待中的线程有竞争锁的资格,注意只是资格,并不代表被唤醒的线程就一定会获得锁。
Condition接口的具体实现还是在AbstractQueuedSynchronizer中的内部实现的——AbstractQueuedSynchronizer$ConditionObject。ConditionObject中维护了一个“等待队列”,注意这个和AQS同步器维护的“同步队列”不同。AQS所维护的同步队列是当前等待资源(同步状态)的队列,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个节点并加入到同步队列中,同时阻塞当前线程,当同步状态被所持有的线程释放时会将同步队列中的首节点唤醒重新获取同步状态。而每个Condition维护一个等待队列,该队列的作用是一个等待signal信号的队列。这两者之间的关系是一个协同的关系,用下图的说明它们之间的协同过程:
1. AQS的同步队列如下图所示,一个头结点head指向队首,一个tail指向队尾,当线程调用lock()方法获取锁而未成功时,线程被构造成节点加入到队尾。(图中NodeA是同步队列的第一个节点,也就是获得同步状态的节点)
2.NodeA调用await()方法时,NodeA从AQS同步队列中移除,自然也就释放了锁,NodeA此时被加入到Condition的等待队列中,等待signal信号,如下图所示。
3.执行完第2步后,此时NodeB在同步队列中处于第一个节点位置,即获取到了锁,如果NodeB此时执行signal(或者signalAll)方法,NodeA将会从Condition等待队列中被移除即被唤醒,加入到同步队列中,此时NodeA仅仅是被唤醒有了在同步队列中争夺资源的资格,并不代表被唤醒后就立即获得锁,如下图所示。
4. 最后NodeB在signal执行完毕后,调用unLock方法释放锁,此时NodeA处于队首,并争夺同步状态。
以上是AQS的“同步队列”和Condition的“等待队列”之间相互协作的过程,下面从源码解析Condition的主要方法await、signal、signalAll。
- public final void await() throws InterruptedException{
- if (Thread.interrupted()) //线程被中断则抛出中断异常
- throw new InterruptedException();
- Node node = addConditionWaiter(); //将线程构造为Node节点
- long savedState = fullyRelease(node); //释放锁,返回同步状态
- int interruptMode = 0;
- while (!isOnSyncQueue(node)) { //循环判断当前节点是否在同步队列中
- LockSupport.park(this);
- if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
- break; //检查节点在处于等待状态时是否被中断
- }
- //在跳出了循环,即被signal唤醒后重新加入了同步队列后,开始重新竞争锁
- if (acquireQueued(node, savedState) && interruptMode != THROW_IE) //acquireQueued自旋获取锁,具体分析见《2.从AbstractQueuedSynchronizer(AQS)说起(1)——独占模式的锁获取与释放》中对获取同步状态的解析
- interruptMode = REINTERRUPT;
- if (node.nextWaiter != null)
- unlinkCancelledWaiters(); //如果节点从等待状态转换为在同步队列中,并且也已经获得了锁,此时将断开此节点后面的等待节点
- if (interruptMode != 0)
- reportInterruptAfterWait(interruptMode);
- }
在获取锁的线程调用await时,首先会将线程构造为Node节点并释放锁,此时线程被移出同步队列加入到Condition等待队列中,接着在第7行就会while循环判断节点是否在同步队列中,当没有线程调用signal方法的时候显然线程不在同步队列,并将一直循环,直到有线程调用signal方法该线程才会被唤醒加入到同步队列中,此时才会跳出循环。
signal和signalAll方法的异同在和notify和notifyAll一样。signal只会唤醒等待队列中位于队首的节点使其具有竞争锁的资格,而signalAll则会唤醒等待队列中所有节点使所有节点都具有竞争锁的资格。
- public final void signal() {
- if (!isHeldExclusively()) //判断当前线程是否持有锁
- throw new IllegalMonitorStateException();
- Node first = firstWaiter;
- if (first != null)
- doSignal(first); //唤醒等待队列中的第一个节点
- }
对比signalAll方法,不同点在于第6行是唤醒等待队列中的所有节点——doSignalAll(first),不再贴出代码。
- private void doSignal(Node first) {
- do {
- if ((firstWaiter = first.nextWaiter) == null)
- lastWaiter = null;
- first.nextWaiter = null;
- } while (!transferForSignal(first) && (first = firstWaiter) != null) //transferForSignal方法将处于等待队列中的节点添加到同步队列中
- }
至于doSignalAll则是循环调用transferForSignal使得所有节点都被唤醒加入到同步队列中。
当节点从等待队列中加入到同步队列中时,呼应await中的循环等待节点是否在同步队列中,await和signal的协同配合也就很清晰明了了。
6.类似Object监视器方法的Condition接口的更多相关文章
- Java 并发编程之 Condition 接口
本文部分摘自<Java 并发编程的艺术> 概述 任意一个 Java 对象,都拥有一个监视器方法,主要包括 wait().wait(long timeout).notify() 以及 not ...
- 并发之lock的condition接口
13.死磕Java并发-----J.U.C之Condition 12.Condition使用总结 11.Java并发编程系列之十七:Condition接口 === 13.死磕Java并发-----J. ...
- Java并发:Condition接口
Condition 接口与 Lock 配合实现了等待 / 通知模式,这个和 Object 的监视器方法(wait.notify.notifyAll 等方法)一样,都是实现了等待 / 通知模式,但这两者 ...
- Lock接口之Condition接口
之前在写显示锁的是后,在显示锁的接口中,提到了new Condition这个方法,这个方法会返回一个Condition对象 简单介绍一下 Condition接口: 任意一个Java对象,都拥有一组监视 ...
- C# IComparable接口、IComparer接口和CompareTo(Object x)方法、Compare()方法
在项目中经常会用到字符串比较,但是有时候对字符串的操作比较多,规则各异.比如有的地方我们需要用排序规则,有的地方需要忽略大小写,我们该如何写一个比较容易操作的比较方法呢?重新实现IComparer接口 ...
- Java的LockSupport工具,Condition接口和ConditionObject
在之前我们文章(关于多线程编程基础和同步器),我们就接触到了LockSupport工具和Condition接口,之前使用LockSupport工具来唤醒阻塞的线程,使用Condition接口来实现线程 ...
- Java根类Object的方法说明
Java中的Object类是所有类的父类,它提供了以下11个方法: public final native Class<?> getClass() public native int ha ...
- Condition接口
<Java并发编程艺术>读书笔记 Condition介绍 任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object中),主要包括wait().wait(long ...
- Condition接口及其主要实现类ConditionObject源码浅析
1.引子 任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait().wait(long timeout).notify()以及notifyAll() ...
随机推荐
- iOS 从url中获取文件名以及后缀
//这里有一个模拟器沙盒路径(完整路径) NSString* index=@"/Users/junzoo/Library/Application Support/iPhone Simulat ...
- ios 渐进淡出
在github上寻找的经典demo //以view为继承类 LazyFadeInView-master https://github.com/itouch2/LazyFadeInView //以Lab ...
- webpack搭建服务器,随时修改刷新
前提:1.对webpack有一定了解,本文不做介绍 2.安装node.js 手把手操作: 1.创建一个名为webpack-server的文件夹(随便取的) 2.cd到当前文件夹:cd webpack- ...
- BeautifulSoup库children(),descendants()方法的使用
BeautifulSoup库children(),descendants()方法的使用 示例网站:http://www.pythonscraping.com/pages/page3.html 网站内容 ...
- angular directive
1.restrict (字符串)可选参数,指明指令在DOM里面以什么形式被声明: 取值有:E(元素),A(属性),C(类),M(注释),其中默认值为A: E(元素):<directiveName ...
- NodeJs系列一:神奇的nodejs
nodejs是什么 nodejs能解决什么问题 非阻塞型I/O及事件环机制 什么时候使用nodejs nodejs是什么 Node.js是让Javascript脱离浏览器运行在服务器的一个平台(或者叫 ...
- 关于 centos 7系统,iptables透明网桥实现【转载请注明】
首先建立网桥:(使用bridge) 示例 桥接eth0 与 eth1 网口 /sbin/modprobe bridge /usr/sbin/brctl addbr br0 /sbin/ifup ...
- LeetCode 207. Course Schedule(拓扑排序)
题目 There are a total of n courses you have to take, labeled from 0 to n - 1. Some courses may have p ...
- 【Spark2.0源码学习】-4.Master启动
Master作为Endpoint的具体实例,下面我们介绍一下Master启动以及OnStart指令后的相关工作 一.脚本概览 下面是一个举例: /opt/jdk1..0_79/ ...
- Express - 入门
Express入门篇 1.HelloWorld 根目录新建server.js文件,插入代码: var express = require('express'); var app = express( ...