[图解Java]Condition

图解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()方法就是把条件队列中的第一个元素, 尾插入到`等待队列`中.

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

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

原文地址:https://www.cnblogs.com/noKing/p/9380091.html

时间: 2024-11-02 16:35:56

[图解Java]Condition的相关文章

图解Java字符串不变性

1. 声明字符串 String s = "abcd"; 这里,s存储了“abcd”在这个字符串对象的引用,如下图所示: 2. 将字符串变量s赋值给字符串变量s2 String s2 = s; 此时,s2也指向了“abcd”. 3. 字符串合并 s = s.concat("ef); 这里,明显是新创建了字符串对象“abcdef”,也就是说,在java中字符串对象一旦被创建就不会改变. 总结 在Java中,一旦一个字符串对象在内存中(通常在堆中)被创建,它就不会被改变.同时字符串

图解 Java IO : 二、FilenameFilter源码

Writer      :BYSocket(泥沙砖瓦浆木匠) 微         博:BYSocket 豆         瓣:BYSocket FaceBook:BYSocket Twitter    :BYSocket 从上一篇 图解 Java IO : 一.File源码 并没有把所有File的东西讲完.这次讲讲FilenameFilter,关于过滤器文件<Think In Java>中写道: 更具体地说,这是一个策略模式的例子,因为list()实现了基本功能,而按着形式提供了这个策略,完

图解Java内存区域及内存溢出异常

图解 Java 内存区域及内存溢出异常 在阅读 <深入理解Java虚拟机:JVM高级特性与最佳实践(第2版) >后,为了加深对 Java 内存区域的印象及理解,特意做成了思维导图. 名词解释 线程共享数据区域 直接内存 并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区.NIO 中使用 Native 函数直接分配堆外内存 方法区 Method Area 用于存放已被虚拟机加载的类信息.常量.静态变量.JIT 编译后的代码等 也称作 永久代,在这块容易遇到 OOM 问题

图解 Java 内存模型

图解 Java 内存模型 (图片来自于:http://www.cnblogs.com/zhangs1986/p/7903722.html) 原文地址:https://www.cnblogs.com/zfc-java/p/8227489.html

从底层入手,图解 Java NIO BIO MIO AIO 四大IO模型与原理

目录 写在前面 1.1. Java IO读写原理 1.1.1. 内核缓冲与进程缓冲区 1.1.2. java IO读写的底层流程 1.2. 四种主要的IO模型 1.3. 同步阻塞IO(Blocking IO) 1.4. 同步非阻塞NIO(None Blocking IO) 1.5. IO多路复用模型(I/O multiplexing) 1.6. 异步IO模型(asynchronous IO) 小结一下: 写在最后 疯狂创客圈 百万级流量 高并发实战 疯狂创客圈 Java 分布式聊天室[ 亿级流量

图解Java设计模式之设计模式面试题

图解Java设计模式之设计模式面试题 1.1 Java设计模式内容介绍 1.1.1 先看几个经典的面试题 1.1.2 设计模式的重要性 1.1 Java设计模式内容介绍 1.1.1 先看几个经典的面试题 原型设计模式问题 :1)有请使用UML类图画出原型模式核心角色2)原型设计模式的深拷贝和浅拷贝是什么.并写出深拷贝的两种方式的源码(重写clone方法实现深拷贝.使用序列化来实现深拷贝)3)在Spring框架中哪里使用到原型模式,并对源码进行分析beans.xml <bean id="id

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

图解Java设计模式之设计模式七大原则 2.1 设计模式的目的 2.2 设计模式七大原则 2.3 单一职责原则 2.3.1 基本介绍 2.3.2 应用实例 2.4 接口隔离原则(Interface Segregation Principle) 2.4.1 基本介绍 2.4.2 应用实例 2.5 依赖倒转原则 2.5.1 基本介绍 2.5.2 应用实例 2.6 里氏替换原则 2.6.1 OO中的继承性的思考和说明 2.6.2 基本介绍 2.6.3 一个程序引出的问题和思考 2.6.4 解决方法 2

图解Java设计模式之原型模式

图解Java设计模式之原型模式 克隆羊的问题 原型模式 - 基本介绍 原型模式在Spring框架中源码分析 浅拷贝的介绍 深拷贝基本介绍 克隆羊的问题 现在有一只羊tom,姓名为 : tom,年龄为 :1,颜色为 :白色,请编写程序创建和tom羊属性完全相同的10只羊. 传统方式解决克隆羊的问题 package com.example.demo.prototype; public class Sheep { private String name; private int age; privat

图解Java设计模式之桥接模式

图解Java设计模式之桥接模式 手机操作问题 传统方案解决手机操作问题 传统方案解决手机操作问题分析 桥接模式(Bridge)-基本介绍 桥接模式解决手机操作问题 桥接模式在JDBC中的源码解析 桥接模式的注意事项和细节 桥接模式其它应用场景 手机操作问题 现在对不同手机类型的不同品牌实现操作编程(比如 :开机.关机.上网.打电话等等),如图 : 传统方案解决手机操作问题 传统方法对应的类图 传统方案解决手机操作问题分析 1)扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各