转载请注明出处。。。

接着上一篇的ReentrantLock和condition源码浅析(一),这篇围绕着condition

一、condition的介绍

在这里为了作对比,引入Object类的两个方法,notify和wait方法,这两个方法作用,估计都很清楚,就是一个具有唤醒线程,另一个具有让线程等待的功能,适用场景,像类似生产者,消费者,那样。

这两个方法的注意点,在于必须放在同步块中执行,具体原因可自行百度或谷歌。执行wait方法后,线程会阻塞,并释放同步代码块的锁(sleep方法会持有锁),notify的方法执行,会唤醒某个线程,但是如果有多个线程执行wait方法阻塞,notify的执行只会唤醒其中某个线程,并不能指定线程唤醒,这时要使用notifyAll才能达到唤醒所有阻塞线程。这样确实有点麻烦,而condition的引入就是为了解决只唤醒执行阻塞的线程。它具有超时作用,即超过某段时间,即会自动唤醒,不会造成一直阻塞。常用的阻塞队列就是这个类实现的。

使用注意点,await和signal必须要在lock方法后执行,如果不执行lock方法就执行await或signal,会出现异常的,具体原因,稍后分析。

二、condition的await方法

在我们使用该方法时,首先会获取Lock接口的一个实现类,然后调用newCondition类方法,本文以ReentrantLock为例。使用方法如下

 Lock lock = new ReentrantLock();

 Condition empty = lock.newCondition();

 // 阻塞线程,并释放锁
empty.await(); // 唤醒前面阻塞的线程
empty.signal();

这里先从await方法(不带参数)开始,代码如下

public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 将当前线程包装成一个Node节点,并节点状态为condition,值为-2
Node node = addConditionWaiter();
// 释放当前线程持有的所有锁
long savedState = fullyRelease(node);
int interruptMode = 0;
// 判断当前线程是否在同步队列中,即在head->tail队列中,如果不在,那就是还在等待队列中,阻塞当前线程。
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 当当前线程执行了signal方法会经过这个,即重新将当前线程加入同步队列中
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}

前面也说了该方法一定在lock方法内部执行,不然就会抛异常。具体代码在fullRelease(node)方法内,代码如下

 final long fullyRelease(Node node) {
boolean failed = true;
try {
// 获取当前线程状态值,即持有了几个锁
long savedState = getState();
// 释放锁,最后最执行到ReentrantLock的tryRelease()方法,该段代码会判断当前线程是与持有锁的线程是同一个线程,如果不是,则抛异常
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
// 抛异常了,则将此节点状态改为canceled,等待从队列中移除
node.waitStatus = Node.CANCELLED;
}
}

该方法的执行,有这几个步骤

1、将当前线程包装成一个node节点,且状态节点为condition,值为-2。

2、释放当前线程持有的所有锁,让下一个线程能获取锁。

3、如果条件满足,则阻塞该线程,等待被唤醒

4、若被唤醒,则尝试加入该节点到同步队列中,直到获取锁。

至于该方法的几个其他的重载方法,这里不过多叙述

不一样的地方就是改造了while()循环内部代码,重点是使用了LockSupport.parkNanos方法能保证在规定时间,该线程会被唤醒。其他判断就是一些当前线程有没有被中断,有没有达到等待时间等。

三、condition的signal方法

该方法用来唤醒执行await方法被阻塞的线程,具体代码如下

 public final void signal() {
// 与await方法一样,如果不在lock方法内执行,则也会抛异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
// 唤醒等待队列中线程
doSignal(first);
} private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
} final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled. 如果改变node节点状态失败,即该节点被取消了
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将该节点加入同步队列中,即head->tail队列中
Node p = enq(node);
int ws = p.waitStatus;
// 如果节点被取消,或更改状态失败,则唤醒被阻塞的线程
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}

或许有的人会有疑问,执行await方法,有执行LockSupport.park方法来阻塞线程,但是执行signal方法顺利的话,没有代码执行LockSupport.unpark方法,那线程岂不是还一直在阻塞中?

其实呢,signal起的作用并不是直接唤醒线程,它的作用是把阻塞的线程移到同步队列中,在上一篇中ReentrantLock和condition源码浅析(一) 博文中有介绍release方法,该方法内部有一个执行unpark方法,

它会去不断的释放满足条件的并被阻塞的线程。

----------------------------------------------------------------------------------------华丽的分界线----------------------------------------------------------------------------------------------------------------------------

以上内容就是我堆condition的了解,若有错误或不足之处,还望指正,谢谢!

ReentrantLock和condition源码浅析(二)的更多相关文章

  1. ReentrantLock和condition源码浅析(一)

    转载请注明出处..... 一.介绍 大家都知道,在java中如果要对一段代码做线程安全操作,都用到了锁,当然锁的实现很多,用的比较多的是sysnchronize和reentrantLock,前者是ja ...

  2. JDK8 BigDecimal API-创建BigDecimal源码浅析二

    第二篇,慢慢来 根据指数调整有效小数位数 // 上一篇由字符串创建BigDecimal代码中,有部分代码没有给出,这次补上 // 这个是当解析字符数组时存在有效指数时调整有小小数位数方法 privat ...

  3. LinkedList类源码浅析(二)

    1.上一节介绍了LinkedList的几个基本的方法,其他方法类似,就不一一介绍: 现在再来看一个删除的方法:remove(Object o) remove方法接受一个Object参数,这里需要对参数 ...

  4. ArrayList类源码浅析(二)

    1.removeAll(Collection<?> c)和retainAll(Collection<?> c)方法 第一个是从list中删除指定的匹配的集合元素,第二个方法是用 ...

  5. Android源码浅析(二)——Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境

    Android源码浅析(二)--Ubuntu Root,Git,VMware Tools,安装输入法,主题美化,Dock,安装JDK和配置环境 接着上篇,上片主要是介绍了一些安装工具的小知识点Andr ...

  6. kernel(二)源码浅析

    目录 kernel(二)源码浅析 建立工程 启动简析 head.s 入口点 查询处理器 查询机器ID 启动MMU 其他操作 start_kernel 处理命令行 分区 title: kernel(二) ...

  7. 二十三、并发编程之深入解析Condition源码

    二十三.并发编程之深入解析Condition源码   一.Condition简介 1.Object的wait和notify/notifyAll方法与Condition区别 任何一个java对象都继承于 ...

  8. Java显式锁学习总结之六:Condition源码分析

    概述 先来回顾一下java中的等待/通知机制 我们有时会遇到这样的场景:线程A执行到某个点的时候,因为某个条件condition不满足,需要线程A暂停:等到线程B修改了条件condition,使con ...

  9. MySQL多版本并发控制机制(MVCC)-源码浅析

    MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<<事务处理-概念与技术>>诚然 ...

随机推荐

  1. Confluence 6 允许其他用户编辑站点欢迎消息

    你可以通过使用 Include Page 宏从你站点其他页面中包含内容,而允许其他不是 Confluence 管理员的用户编辑站点欢迎消息.使用这种方式能够避免直接对模板文件中的内容进行编辑. 从站点 ...

  2. Confluence 6 自定义 Decorator 模板的宏和针对高级用户

    宏 页面的某些部分使用的是 Velocity  宏进行创建的,包括导航栏.有关宏的创建,你可以参考页面 Working With Decorator Macros 页面中的内容. 针对高级用户 vel ...

  3. 关于ios进入后台界面后 播放声音解决方案

    1 最近我在做环信视频通话时,遇到了一个新功能就是APP在后台的时候能对方能视频或者音频过来的时候 能够播放声音 根据查询相关资料得到如下解决办法 NSError *error; AVAudioSes ...

  4. elementui上传图片到七牛云服务器

    注册七牛云 首先,注册七牛云,并且完成实名认证,完成后会在个人中心->秘钥管理中看到两个秘钥AccessKey/SecretKey 创建存储空间(必须要实名认证) 生成上传凭证 为了实现上传,我 ...

  5. nodejs之glob与globby

    glob glob允许使用规则,从而获取对应规则匹配的文件.这个glob工具基于javascript.它使用了 minimatch 库来进行匹配 安装 npm install glob 引入 cons ...

  6. 雅礼 noip2018 模拟赛 day3 T3

    典型树形dp 这里,我们应该看到一些基本性质: ①:如果这个边不能改(不是没有必要改),我们就不改,因为就算改过去还要改回来,显然不是最优的 注意:"不能改"是指边的性质和要求的相 ...

  7. Sequence Number

    1570: Sequence Number 时间限制: 1 Sec  内存限制: 1280 MB 题目描述 In Linear algebra, we have learned the definit ...

  8. Nginx详解七:Nginx基础篇之Nginx官方模块

    Nginx官方模块 --with-http_stub_status_module:Nginx的客户端状态,用于监控连接的信息,配置语法如下:配置语法:stub_status;默认状态:-配置方法:se ...

  9. windows 下命令行启动停止mysql

    MySQL比较好玩一点就是它可以用多种方式启动,当然它也可以用多种方式关闭.下面我就mysql的几种启动方式简单的谈一谈,希望可以给大家提供一些参考. 第一种,用mysqld-nt来启动. 在没有进行 ...

  10. Unet 项目部分代码学习

    github地址:https://github.com/orobix/retina-unet 主程序: ################################################ ...