线程间的调度顺序

news2024/11/19 3:23:18

目录

♫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.coloradmin.cn/o/1138360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3响应式对象:ref和reactive

ref() <template><button click"changeMsg">改变信息</button><div>{{ msg }}</div><div>{{ man }}</div> </template><script lang"ts"> import { defineComponent,ref,Ref } from vueexport def…

Android 11.0 禁用插入耳机时弹出的保护听力对话框

1.前言 在11.0的系统开发中,在某些产品中会对耳机音量调节过高限制,在调高到最大音量的70%的时候,会弹出音量过高弹出警告,所以产品 开发的需要要求去掉这个音量弹窗警告功能 2.禁用插入耳机时弹出的保护听力对话框的核心类 frameworks\base\packages\SystemUI\src\com\and…

在el-dialog中使用tinymce 点击工具栏下拉框被遮挡

在el-dialog中使用tinymce控件时&#xff0c;会出现点击工具栏下拉框出现在弹窗下一层&#xff0c;审查元素之后发现是tinymce的下拉框z-index优先级低于el-dialog的z-index导致的&#xff0c;所以需要增加tinymce的下拉框的z-index值。 通过审查元素得到&#xff0c;需要修改t…

物联网设备安全性:构建可信任的智能生态系统

第一章&#xff1a;引言 物联网&#xff08;IoT&#xff09;已经成为现代社会的重要组成部分&#xff0c;将我们的生活变得更加智能化和便利。从智能家居到工业自动化&#xff0c;物联网设备正日益渗透到各个领域。然而&#xff0c;随着物联网设备的普及&#xff0c;安全性问题…

牛客网刷题-(5)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

Spark On Hive原理和配置

目录 一、Spark On Hive原理 &#xff08;1&#xff09;为什么要让Spark On Hive&#xff1f; 二、MySQL安装配置&#xff08;root用户&#xff09; &#xff08;1&#xff09;安装MySQL &#xff08;2&#xff09;启动MySQL设置开机启动 &#xff08;3&#xff09;修改MySQL…

常用应用安装教程---在centos7系统上安装JDK8

在centos7系统上安装JDK8 1&#xff1a;进入oracle官网下载jdk8的tar.gz包&#xff1a; 2&#xff1a;将下载好的包上传到每个服务器上&#xff1a; 3&#xff1a;查看是否上传成功&#xff1a; [rootkafka01 ~]# ls anaconda-ks.cfg jdk-8u333-linux-x64.tar.gz4&#xf…

Ps:简单快速的主背分离方法

将主体与背景分离开来&#xff0c;可大大提高后期调色修片的效率。本文介绍的方法&#xff0c;简单快速&#xff0c;实用性强。 ◆ ◆ ◆ 主背分离一般步骤及说明 1、复制背景图层两次&#xff0c;分别命名为&#xff1a;“主体”和“新背景”。隐藏原背景图层。 2、选取主体…

华为NAT配置实例(含dhcp、ospf配置)

一、网络拓朴如下&#xff1a; 二、要求&#xff1a;PC1 能访问到Server1 三、思路&#xff1a; R2配置DHCP&#xff0c;R2和R1配OSPF&#xff0c;R1出NAT 四、主要配置&#xff1a; R2的DHCP和OSPF&#xff1a; ip pool 1gateway-list 10.1.1.1 network 10.1.1.0 mask 25…

进程/线程/PCB

进程&#xff1a;正在运行中的程序&#xff08;进程是驻留在内存中的&#xff09; 是系统执行资源分配和调度的独立单位每一个进程都有属于自己的存储空间和系统资源注意&#xff1a;进程A 和 进程B 的内存独立不共享 使用jdk自带的工具&#xff0c;jconsole查看当前Java进程中…

【C++】STL容器——vector类的使用指南(含代码演示)(11)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一、vector类——基本介绍二、vector类…

日本IT Week秋季展丨美格智能以技术创新共建美好数字生活

10月25日至27日&#xff0c;日本国际IT消费电子展览会&#xff08;Japan IT Week 2023秋季展&#xff09;在日本千叶幕张国际展览中心举行。日本IT周是日本IT市场的标杆&#xff0c;涵盖软件开发、大数据管理、嵌入式系统、数据存储、信息安全、数据中心、云计算、物联网&#…

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

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

嵌入式系统设计师考试笔记之操作系统基础复习笔记二

目录 3、任务管理 &#xff08;1&#xff09;嵌入式操作系统的任务管理可以分为 &#xff08;2&#xff09;进程 &#xff08;3&#xff09;线程 &#xff08;4&#xff09;任务 &#xff08;5&#xff09;任务的创建与中止 &#xff08;6&#xff09;任务的状态任务有三…

Spring Boot 使用 Disruptor 做内部高性能消息队列

这里写自定义目录标题 一 、背景二 、Disruptor介绍三 、Disruptor 的核心概念3.1 Ring Buffer3.2 Sequence Disruptor3.3 Sequencer3.4 Sequence Barrier3.5 Wait Strategy3.6 Event3.7 EventProcessor3.8 EventHandler3.9 Producer 四、案例-demo五、总结 一 、背景 工作中遇…

rust入门

一&#xff0c;输入输出 println!("Hello, World"); 二&#xff0c;函数 1&#xff0c;main函数 fn main() {println!("Hello, World"); }2&#xff0c;普通函数 fn myPrint(){println!("{}", 1234); } fn main() {myPrint(); }3&#xff0…

AI新能量!FortiGate NGFW面向数据中心全面集成FortiGuard AI 安全服务

企业IT技术正在以惊人的速度发展&#xff0c;转型最大的领域之一是下一代防火墙&#xff08;NGFW&#xff09;市场。如今&#xff0c;混合云、多云、边缘等多种基础设施形态共存&#xff0c;已经成为大部分企业的常态&#xff0c;不断扩张的攻击面需要不同形态防火墙的安全防护…

一个简单高效低内存的.NET操作Excel开源框架 - MiniExcel

前言 日常工作中经常与数据打交道的同学肯定会难以避免对Excel的一些数据操作如导入、导出等&#xff0c;但是当对一些大数据量操作Excel时经常会遇到一个常见的问题内存溢出。今天给大家推荐一个简单、高效、低内存避免OOM&#xff08;内存溢出&#xff09;的.NET操作Excel开…

PDF 文档处理:使用 Java 对比 PDF 找出内容差异

不论是在团队写作还是在个人工作中&#xff0c;PDF 文档往往会经过多次修订和更新。掌握 PDF 文档内容的变化对于管理文档有极大的帮助。通过对比 PDF 文档&#xff0c;用户可以快速找出文档增加、删除和修改的内容&#xff0c;更好地了解文档的演变过程&#xff0c;轻松地管理…

FL Studio21.2官方重磅更新及新功能一分钟介绍

好消息&#xff01;FL Studio21.2 在 10 月 26 日正式发布啦&#xff0c;它新增了 FL Cloud 在线采样库和 AI 音乐制作功能&#xff0c;还提供音乐分发到 Spotify、Apple Music 等主要音乐平台的服务。此外&#xff0c;还有新的音频分离功能、自定义波形颜色和新的合成器 Keple…