线程间的调度顺序

目录

♫join和sleep

♫wait

♫notify和notifyAll


我们知道线程是抢占式执行,随机调度的,而这也是诱发线程安全的根本原因。我们虽然无法指定线程之间的调度顺序,但是可以通过JVM提供的一些API让某个线程阻塞,主动放弃CPU,给其他线程让路,从而间接调整线程间的调度顺序。

♫join和sleep

我们已经知道通过join可以让一个线程等待令另一个线程执行完毕/一定时间,sleep可以让线程阻塞指定时间:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            try {
                //让t1线程休眠100毫秒
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("t1线程执行完毕");
        });
        t1.start();
        //主线程等待t1执行完毕(超过500毫秒不再等待)
        t1.join(500);
        System.out.println("主线程执行完毕");
    }
}

运行结果:

 但使用joinsleep无法精确控制线程执行到哪行代码再进入阻塞状态,使用waitnotify/notify就可以更精确地控制线程进入阻塞状态的时间。

wait

wait可以让一个线程暂停执行,等待另一个线程调用notify或notifyAll唤醒它。wait的执行顺序是先释放锁→阻塞等待→收到通知后重写尝试获取锁调,如果没有锁就使用wait运行时会报错:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        o1.wait();
    }
}

运行结果:

非法的监视器(synchronized)状态异常,故wait得先获取锁后使用:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        synchronized (o1) {
            System.out.println("wait前");
            o1.wait();
            System.out.println("wait后");
        }
    }
}

运行结果:

此时,wait就进入阻塞状态,等待notify或notifyAll唤醒它。

注:wait释放锁,进入阻塞状态后,其它线程是可以获取到o1对象的锁的

使用wait时还可以设定等待通知的时间,避免死等:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        synchronized (o1) {
            System.out.println("wait前");
            //500毫秒后仍没有通知则重新尝试获取锁
            o1.wait(500);
            System.out.println("wait后");
        }
    }
}

运行结果:

此时,主线程只会等待通知500毫秒,500毫秒后不需要notify或notifyAll就可以重新尝试获取锁。

注:虽然wait(500)与sleep(500)很像,但wait(500)通过notify或notifyAll唤醒不会抛出异常,属于正常的代码,而sleep(500)虽然能通过interrupt唤醒,但是却是以异常的形式唤醒的,属于出问题的代码。

♫notify和notifyAll

notify用来唤醒wait等待的线程,使用notify同样得先获取锁,且使用notify的对象需要和使用wait的对象一致:

public class Test {
    public static void main(String[] args) {
        Object o1 = new Object();
        Thread t1 = new Thread(()->{
            synchronized (o1) {
                System.out.println("wait前");
                try {
                    o1.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("wait后");
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (o1) {
                o1.notify();
            }
        });

        t1.start();
        t2.start();
    }
}

运行结果:

此处,先执行t1线程,t1掉用wait方法阻塞后执行t2,t2调用notify方法后唤醒t1线程。但由于线程的随机调度,并不能保证t1线程比t2线程先执行,如果是t2线程先执行的话,notify先执行,那么notify相当于是无效通知,等到t1线程调用wait方法后就没有对应的notify唤醒了。

注:在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

若有多个线程在等待o1对象,o1.notify则是随机唤醒一个等待的线程:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        Thread t1 = new Thread(()->{
            synchronized (o1) {
                try {
                    System.out.println("t1 wait前");
                    o1.wait();
                    System.out.println("t1 wait后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (o1) {
                try {
                    System.out.println("t2 wait前");
                    o1.wait();
                    System.out.println("t2 wait后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
        //由于线程进入wait的等待状态会释放锁,故t1和t2都会一起进入wait的等待状态
        //让t1和t2都进入wait的等待状态
        Thread.sleep(100);
        synchronized (o1) {
            o1.notify();
        }
    }
}

运行结果:

使用o1.notifyAll则是唤醒所有等待的线程:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        Thread t1 = new Thread(()->{
            synchronized (o1) {
                try {
                    System.out.println("t1 wait前");
                    o1.wait();
                    System.out.println("t1 wait后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (o1) {
                try {
                    System.out.println("t2 wait前");
                    o1.wait();
                    System.out.println("t2 wait后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
        //由于线程进入wait的等待状态会释放锁,故t1和t2都会一起进入wait的等待状态
        //让t1和t2都进入wait的等待状态
        Thread.sleep(100);
        synchronized (o1) {
            o1.notifyAll();
        }
    }
}

运行结果:

调用interrupt方法也可以导致 wait 抛出 InterruptedException 异常而终止等待:

public class Test {
    public static void main(String[] args) {
        Object o1 = new Object();
        Thread t1 = new Thread(()->{
            synchronized (o1) {
                try {
                    System.out.println("wait前");
                    o1.wait();
                    System.out.println("wait后");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        t1.interrupt();
    }
}

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/105555.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【31】c++设计模式——>模板方法模式

模板方法模式通常由以下几个部分组成: 1.抽象基类(Abstract Base Class):抽象基类定义了一个算法的骨架,其中包含了模板方法和一些基本操作方法。模板方法在抽象基类中被声明为虚函数,它定义了算法的流程&…

CentOS - 安装 Elasticsearch

"Elasticsearch"是一个流行的开源搜索和分析引擎,它可以用于实时搜索、日志和事件数据分析等任务。以下是在 CentOS 上安装 Elasticsearch 的基本步骤: 安装 Java: Elasticsearch 是基于 Java 的应用程序,所以首先需要…

openwrt下游设备在校园网(DLUT-LingShui)中使用ipv6网络

背景:校园网最多支持6台设备的无感认证,需要使用路由器(本人使用openwrt系统)为更多的设备提供网络,但校园网分配的ipv6地址子网为/128,不能为路由器下的设备分配全球ipv6地址,因此需要使用nat6转发下游设备的局域网ip…

el-table多选表格 实现默认选中 删除选中列表取消勾选等联动效果

实现效果如下&#xff1a; 代码如下&#xff1a; <template><div><el-tableref"multipleTable":data"tableData"tooltip-effect"dark"style"width: 100%"selection-change"handleSelectionChange"><…

Generative AI 新世界 | Falcon 40B 开源大模型的部署方式分析

在上期文章&#xff0c;我们探讨了如何在自定义数据集上来微调&#xff08;fine-tuned&#xff09;模型。本期文章&#xff0c;我们将重新回到文本生成的大模型部署场景&#xff0c;探讨如何在 Amazon SageMaker 上部署具有 400 亿参数的 Falcon 40B 开源大模型。 亚马逊云科技…

IC-705连接wfview

wfview是一款开源的主要针对ICOM的远程控制软件&#xff0c;可以通过USB或者无线控制电台&#xff0c;貌似还支持X6100。 IC-705支持WLAN功能&#xff0c;连接wfview非常方便。 IC-705的WLAN支持两种模式&#xff0c;一种是Station模式&#xff0c;可用于连接WI-FI路由器&#…

【考研数学】数学“背诵”手册 | 需要记忆且容易遗忘的知识点

文章目录 引言一、高数常见泰勒展开 n n n 阶导数公式多元微分函数连续、可微、连续可偏导之间的关系多元函数极值无条件极值条件极值 三角函数的积分性质华里士公式&#xff08; “点火”公式 &#xff09;特殊性质 原函数与被积函数的奇偶性结论球坐标变换公式 二、写在最后 …

python自动化测试(四):ECShop后台:商品分类添加

前置条件&#xff1a; 本地部署&#xff1a;ECShop的版本是3.0.0、Google版本是 Google Chrome65.0.3325.162 (正式版本) &#xff08;32 位&#xff09; Google驱动的selenium版本是3.11.0 目录 前置代码 一、登录&#xff08;后台登录&#xff09; 二、进入商品分类页…

JavaScript笔记(本文中将JavaScript简写为JS)

JS对大小写敏感 JS代码块的作用域都是全局的 JS的数组只能使用数字作为下标 JS对浮点型数据的精确度很难确定 JS在定义数组元素以及对象&#xff0c;在最后不能添加逗号 JS 中&#xff0c;变量可以在使用后声明&#xff0c;也就是变量可以先使用再声明&#xff0c;但不适用于已…

wkhtmltoimage/wkhtmltopdf 使用实践

1. 介绍 wkhtmltopdf/wkhtmltoimage 用于将简单的html页面转换为pdf或图片&#xff1b; 2.安装 downloads 2.1. mac os 下载64-bit 版本然后按照指示安装, 遇到 untrust developers 时&#xff0c;需要在 Settings -> Privacy 处信任下该安装包。 2.2. debian # 可用…

vscode代码快捷输入

Vscode代码片段快捷输入 常用的代码片段为了避免重复输入,可以使用Vsco的中用户代码片段进行设置,这样就可以实现快捷输入. 操作流程 如下 打开vscode的设置 2. 找到用户代码片段 3. 选择模板 4. 然后写入代码片段即可 上面的代码片段可以设置多个,看自己 重点关注的是 prefi…

05 MIT线性代数-转置,置换,向量空间Transposes, permutations, spaces

1. Permutations P: execute row exchanges becomes PA LU for any invertible A Permutations P identity matrix with reordered rows mn (n-1) ... (3) (2) (1) counts recordings, counts all nxn permuations 对于nxn矩阵存在着n!个置换矩阵 , 2. Transpose: 2.…

p5.js 3D图形-立方体

本文简介 带尬猴&#xff0c;我嗨德育处主任 前面写了几篇 p5.js 文章 都还没涉及到3D图形&#xff0c;但其实 p5.js 是提供了基础的3D图形的。 本文就从最简单的立方体讲起&#xff0c;并做几个小demo和各位工友一起掌握立方体的用法。 立方体的基础用法 在 p5.js 里使用 b…

Ubuntu 内核降级到指定版本

reference https://www.cnblogs.com/leebri/p/16786685.html 前往此网站&#xff0c;找到所需的内核 https://kernel.ubuntu.com/~kernel-ppa/mainline/ 查看系统架构 dpkg --print-architecture 二、下载安装包 注意&#xff1a;下载除lowlatency以外的deb包 三、安装内核 3…

基于图像识别的跌倒检测算法 计算机竞赛

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于图像识别的跌倒检测算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/…

FreeRTOS 事件标志组 详解

目录 什么是事件标志组&#xff1f; 事件标志位 事件标志组 事件标志组相关 API 函数 1. 创建事件标志组 2. 设置事件标志位 3. 清除事件标志位 4. 等待事件标志位 事件标志组实操 什么是事件标志组&#xff1f; 事件标志位 表明某个事件是否发生&#xff0c;联想&am…

优咔科技创新连接方案助力高质量5G车联服务

上海优咔网络科技有限公司 CEO 闫楠 【摘要】本文就智能网联汽车对高质量5G车联服务的需求背景和行业趋势进行了分析&#xff0c;主要介绍采用5G双SIM卡的创新连接方案&#xff0c;重点讲述双SIM卡联网的端到端体系架构和技术方案&#xff0c;并就优咔科技全方位支撑行业领先车…

设计模式—创建型模式之单例模式

设计模式—创建型模式之单例模式 介绍 单例模式说明&#xff1a;一个单一的类&#xff0c;负责创建自己的对象&#xff0c;同时确保系统中只有单个对象被创建。 单例模式特点&#xff1a; 某个类只能有一个实例&#xff1b;&#xff08;构造器私有&#xff09;它必须自行创…

【抓包分析】通过ChatGPT解密还原某软件登录算法实现绕过手机验证码登录

文章目录 &#x1f34b;前言实现效果成品广告抓包分析一、定位加密文件二、编辑JS启用本地替换 利用Chatgpt进行代码转换获取计划任务id模拟数据请求最后 &#x1f34b;前言 由于C站版权太多&#xff0c;所有的爬虫相关均为记录&#xff0c;不做深入&#xff01; 今天发现gith…

读图数据库实战笔记01_初识图

1. 图论 1.1. 起源于莱昂哈德欧拉在1736年发表的一篇关于“哥尼斯堡七桥问题”的论文 1.2. 要解决这个问题&#xff0c;该图需要零个或两个具有奇数连接的节点 1.3. 任何满足这一条件的图都被称为欧拉图 1.4. 如果路径只访问每条边一次&#xff0c;则该图具有欧拉路径 1.5…