图解Condition

0. demo

我先给出一个demo, 这样大家就可以根据我给的这段代码, 边调试边看源码了. 还是那句话: 注意"My" , 我把ReentrantLock类 改名为了 "MyReentrantLock"类 , "Lock"类 改名为了"MyLock"类. 大家粘贴我的代码的时候, 把相应的"My"都去掉就好了, 否则会编译报错哦.

import java.util.Scanner;
import java.util.concurrent.locks.Condition;
import java.util.function.Supplier; public class ConditionTest {
static final Scanner scanner = new Scanner(System.in);
static volatile String cmd = "";
private static MyReentrantLock lock = new MyReentrantLock(true);
private static Condition condition = lock.newCondition(); public static void main(String[] args) {
for (String name : new String[]{"w1", "w2", "w3", "w4", "w5", "w6"})
new Thread(() -> func(() -> lock, name)).start();
new Thread(() -> signalOne(() -> lock, "s")).start(); while (scanner.hasNext()) {
cmd = scanner.nextLine();
}
} public static void func(Supplier<MyLock> myLockSupplier, String name) {
blockUntilEquals(() -> cmd, name);
myLockSupplier.get().lock(); System.out.println(name + "阻塞等待...");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("释放了" + name); myLockSupplier.get().unlock();
} public static void signalOne(Supplier<MyLock> myLockSupplier, String name) {
while (true) {
blockUntilEquals(() -> cmd, name);
myLockSupplier.get().lock();
condition.signal();
System.out.println("通知唤醒了一个等待...");
myLockSupplier.get().unlock();
}
} private static void blockUntilEquals(Supplier<String> cmdSupplier, final String expect) {
while (!cmdSupplier.get().equals(expect))
quietSleep(1000);
clearCmd();
} private static void quietSleep(int mills) {
try {
Thread.sleep(mills);
} catch (InterruptedException e) {
e.printStackTrace();
}
} private static void clearCmd() {
cmd = "";
}
}

使用例子在下面.

首先输入w1, 让线程1执行await() . 然后输入w2, 让线程2执行await(). 然后输入w3, 让线程3执行await().

接下来输入3次 s, 没输入一次s, 并按下回车, 就会signal通知一个await等待.

1. 开始图解Condition

想用ReentrantLock的Condition, 那么就首先要有个ReentrantLock锁.

实例化一个锁, ReentrantLock里只有一个成员变量sync.

sync实例里面有四个成员变量.

分别表示:

1. state - 锁计数器

2. exclusiveOwnerThread - 锁的持有线程

3. head - `等待队列`的头结点.

4. tail - 指向`等待队列`的最后一个元素

然后咱们实例化了一个Condition.

当咱们输入w1后, 第一个线程就申请了锁, 并且申请成功.

然后就执行到了await()方法.

将线程1封装为Node节点, 然后waitState置为-2.  -2的含义是Condition.

await()方法内部的第一个步骤就是把当前线程(线程1)插入到了`条件队列`中.

然后就开始释放当前线程(线程1)的锁了, 而且是完全释放, 一次就释放掉全部重入次数哦, 也就是直接让state等于0.

释放完锁了, 然后挂起线程1.

然后让线程2进行await.( 也就是前面的demo程序中在控制台输入了w2.)

线程2执行await()之前当然是先获取锁了.

由于此时, 锁是空闲的. 所以线程2成功获取到了锁. 淡橙色的阴影部分为变化的内容:

获取锁之后, 线程2就该执行await()了:

如上图, 将线程封装为Node, 然后尾插到`条件队里`中, 只是await() 方法的第一步.

然后的操作, 就是完全释放线程2的锁, 然后挂起线程.

如果这个时候咱们在上面demo程序的控制台输入"s", 那么就会让线程s 申请锁, 申请成功后, 就会执行signal.

首先是线程s申请锁成功:

线程s成功获取了锁以后, 就是该执行signal()了.

首先将`条件队列`里的第一个节点脱离出来:

然后把waitState从-2改为0 :

随后要做的就是把从`条件队列`中脱离出来的Node(就是线程1对应的Node节点), 尾插到`等待队列`中.

但是`等待队列`此时还未被初始化, 所以插入到`等待队列`之前, 要把`等待队列`初始化了. 见下图:

`等待队列`初始化完了. 接下来就是把线程1对应的Node, 尾插到`等待队列`中了:

然后将当前尾插的那个节点的前驱的waitState置为-1.  -1表示下一个节点等待着被唤醒.

接下来就是线程s会执行到unlock(). 然后就会释放锁, 之后就是唤醒`等待队列`中的第一个线程.

如此就介绍完了signal()

一个signal命令, 就把一个await的线程从`条件队列中`移到了`等待队列`中. 到了等待队列中之后, 剩下的就是跟"锁解锁后, 唤醒下一个执行"这样的步骤一样了.

我觉得await方法就是将线程尾插到`条件队列`中. signal()方法就是把条件队列中的第一个元素, 尾插入到`等待队列`中.

所以我觉得不必往下分析了.

也可能是我理所当然了, 有问题的话之后再补充.

[图解Java]Condition的更多相关文章

  1. 图解 Java IO : 二、FilenameFilter源码

    Writer      :BYSocket(泥沙砖瓦浆木匠) 微         博:BYSocket 豆         瓣:BYSocket FaceBook:BYSocket Twitter   ...

  2. 图解 Java 内存模型

    图解 Java 内存模型 (图片来自于:http://www.cnblogs.com/zhangs1986/p/7903722.html)

  3. [温故]图解java多线程设计模式(一)

    去年看完的<图解java多线程设计模式>,可惜当时没做笔记,导致后来忘了许多东西,打算再温习下这本书,顺便在这里记录一下~  1.顺序执行.并行.并发 顺序执行:多个操作按照顺序依次执行. ...

  4. 【转载】图解Java常用数据结构(一)

    图解Java常用数据结构(一)  作者:大道方圆 原文:https://www.cnblogs.com/xdecode/p/9321848.html 最近在整理数据结构方面的知识, 系统化看了下Jav ...

  5. 图解java面试

    图解Java面试题:基本语法 2017-02-07 14:34 出处:清屏网 人气:178 评论(0)   内容大纲.png &和&&的区别 &和&&的 ...

  6. 图解Java继承内存分配

    图解Java继承内存分配   继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类. (2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法. (3)子 ...

  7. (转载)图解Java多态内存分配以及多态中成员方法的特点

    图解Java多态内存分配以及多态中成员方法的特点   图解Java多态内存分配以及多态中成员方法的特点   Person worker = new Worker(); 子类实例对象地址赋值给父类类型引 ...

  8. 图解Java设计模式之UML类图

    图解Java设计模式之UML类图 3.1 UML基本介绍 UML图 UML类图 3.1 UML基本介绍 1)UML – Unified modeling language UML(统一建模语言),是一 ...

  9. 图解Java设计模式之设计模式七大原则

    图解Java设计模式之设计模式七大原则 2.1 设计模式的目的 2.2 设计模式七大原则 2.3 单一职责原则 2.3.1 基本介绍 2.3.2 应用实例 2.4 接口隔离原则(Interface S ...

随机推荐

  1. spark2.4 分布式安装

    一.Spark2.0的新特性Spark让我们引以为豪的一点就是所创建的API简单.直观.便于使用,Spark 2.0延续了这一传统,并在两个方面凸显了优势: 1.标准的SQL支持: 2.数据框(Dat ...

  2. 想知道谁是你的最佳用户?基于Redis实现排行榜周期榜与最近N期榜

    本文由云+社区发表 前言 业务已基于Redis实现了一个高可用的排行榜服务,长期以来相安无事.有一天,产品说:我要一个按周排名的排行榜,以反映本周内用户的活跃情况.于是周榜(按周重置更新的榜单)诞生了 ...

  3. dom4j创建和解析xml文档

    DOM4J解析  特征: 1.JDOM的一种智能分支,它合并了许多超出基本XML文档表示的功能. 2.它使用接口和抽象基本类方法. 3.具有性能优异.灵活性好.功能强大和极端易用的特点. 4.是一个开 ...

  4. zabbix调用api检索方法

    环境 zabbix:172.16.128.16:zabbix_web:172.16.16.16/zabbix 用户名:Admin 密码:zabbix 获取的数据仅做参考,以Linux发送HTTP的PO ...

  5. File operations 1

    1:只读(‘r' 和 ’rb'以字节读) f = open('d:\模特主妇护士班主任.txt',mode='r',encoding='UTF-8') content = f.read() print ...

  6. 基于IPv6的数据包分析(第三组)

    一.实验拓扑 二.配置过程 本处提供R1.R2.R4的详细配置过程(包含静态路由的配置) 1)      R1: R1(config)#int e1/0 R1(config-if)#ipv6 addr ...

  7. 牛津初阶字典单词F-联想故事

    从前有一个fable寓言,讲的是奥巴马穿着棉fabrics织物,走在去往学校的路上,他的心情fabulous极好的,绝妙的.因为他学校的facilities 设施fabulous非常棒,但有些人不喜欢 ...

  8. pyhton崩溃的第六天,又有新成员

    首先在今天的刚刚开始,补充一下上次两个成员的一些特有的方法,一个是列表,一个是字典,首先列表里面多了一个反转的方法,名叫reverse,简单就是把列表中的123变成了321,还有一个方法是sort,是 ...

  9. [POI2015]PUS

    嘟嘟嘟 这题只要往正确的方面想,就很简单. 首先,这是一道图论题! 想到这,这题就简单了.对于两个数\(i\)和\(j\),如果\(i\)比\(j\)大,就从\(i\)向\(j\)连边.然后如果图中存 ...

  10. 单列集合类的根接口Collection

    Collection接口 Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合.JDK 不提供此接 ...