JavaEE 初阶篇-深入了解多线程等待与多线程状态

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 线程等待

        1.1 线程等待 - join() 方法

        1.1.1 main 线程中等待多个线程

        1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程

        1.1.3 其他线程阻塞等待 main 线程

        1.1.4 在指定的时间内阻塞等待

        1.2 线程等待 - Thread.sleep() 方法

        2.0 线程状态

        2.1 新建状态 - NEW

        2.2 就绪状态 - Runnable

        2.3 终止状态 -Terminated

        2.4 等待状态 - Waiting

        2.5 超时等待状态 - Time_Waiting

        2.6 阻塞状态 - Blocked

        2.7 线程状态之间的相互转换图


        1.0 线程等待

        在线程编程中,线程等待是指一个线程暂停执行,直到某个条件满足或者其他线程执行完毕后再继续执行。线程等待的方法:join() 方法Thread.sleep() 方法等等

        1.1 线程等待 - join() 方法

       join() 方法是 Thread 类的一个方法,用于让一个线程等待另一个线程执行完毕。当在一个线程对象上调用 join() 方法时,当前线程会被阻塞,直到被调用的线程执行完毕。

        具体来说,调用 thread.join() 会使当前线程等待 thread 线程执行完毕。如果 thread 线程已经执行完毕,那么 join() 方法会立即返回;如果 thread 线程还在执行,当前线程会被阻塞,直到 thread 线程执行完毕。

代码如下:

public class demo1 {


    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(()->{
            for (int i = 0; i < 1000; i++) {
                System.out.println("正在执行 thread 线程");
            }
        });

        thread.start();
        thread.join();
        System.out.println("执行 main 线程");

    }
}

        main 线程调用 thread.join() ,就会阻塞 main 线程继续执行,会让 thread 线程执行完毕之后,main 线程解除阻塞,继续执行下去。

运行结果:

补充: main 中调用 join() 方法,有以下可能性:

        1.0 如果 t 线程此时已经结束了,此时 join() 方法就会立即返回。

        2.0 如果 t 线程此时还没有结束,此时 main 就会阻塞等待,一直等待到 t 线程结束之后,main 线程才会接触阻塞,继续执行。

        3.0 如果调用等待阻塞的线程对象还没创建 pcb 的时候(即还没 start() 的时候),那么调用 join() 方法的线程会直接解除阻塞。

        1.1.1 main 线程中等待多个线程

        在 main 线程中多次调用 join() 方法时,先执行 t1.join(),如果 t1 还没结束,main 继续阻塞等待,t1 结束之后,继续执行 t2.join() 方法,再等待 t2 结束。注意,t1 与 t2 之间同样是抢占式执行随机调度,所以先后顺序对于 main 线程来说没有什么区别。

代码如下:

public class demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t1 线程");
            }
        });

        Thread t2 = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t2 线程");
            }
        });

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

        t1.join();
        t2.join();

        System.out.println("正在执行 main 线程");
    }
}

运行结果:

        1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程

        这种情况是按顺序执行的,大致就是串行执行一样。顺序为:先要执行完 t1 再执行 t2 最后再执行 main 线程。

代码如下:

public class demo3 {
    public static void main(String[] args) throws InterruptedException {


        Thread t1 = new Thread(()->{

            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t1 线程");
            }
        });

        Thread t2 = new Thread(()->{

            try {
                t1.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t2 线程");
            }

        });

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

        t2.join();
        System.out.println("执行 main 线程");
    }
}

        t1.start() 立即创建 t1 线程,再 t2.start() 创建线程,t1 没有任何阻塞就会直接执行代码,而 t2 遇到了阻塞,需要等待 t1 执行完毕之后,t2 才会解除阻塞,同时由于 main 线程阻塞了,需要等待 t2 执行完毕,当 t2 执行完毕之后,main 线程解除阻塞了,执行 main 线程中的代码。

运行结果:

        1.1.3 其他线程阻塞等待 main 线程

        t1 线程阻塞等待 main 线程执行完毕之后,再执行 t1 线程。

代码如下:

public class demo4 {

    public static void main(String[] args) throws InterruptedException {
        //拿到当前 main 线程对象
        Thread mainThread = Thread.currentThread();

        Thread t1 = new Thread(()->{

            //在 t1 线程中调用 join() 方法,
            //阻塞当前 t1 线程,等待 main 线程执行完毕
            try {
                mainThread.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 t1 线程");
            }

        });
        t1.start();

        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println("正在执行 main 线程");
        }

    }
}

运行结果:

        1.1.4 在指定的时间内阻塞等待

        join() 方法还有一个重载的版本,可以指定一个超时时间,即 join(long millis),表示当前线程最多等待 millis 毫秒,如果超过这个时间 thread 线程还没有执行完毕,当前线程会继续往下执行。简单来说,即使 thread 这个线程还没有结束,main 线程都不会继续等待了;如果 thread 在规定的时间内提前结束,那么 main 也会提前解除阻塞。

代码如下:

public class demo5 {
    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(()->{
            for (int i = 0; i < 1000; i++) {
                System.out.println("正在执行 thread 线程");
            }
        });

        thread.start();

        //等待 0.1 s 就解除阻塞 main 线程,即不会继续等待了
        thread.join(1000);
        System.out.println("正在执行 main");
    }
}

运行结果:

        1.2 线程等待 - Thread.sleep() 方法

        是一个静态方法,它使当前线程暂停执行一段时间。该方法接受一个以毫秒为单位的时间参数,指定线程暂停的时间长度。在这段时间内,线程不会执行任何操作,但是线程的状态仍然是 Runnable 状态,可以随时被调度器调度执行。

        需要注意的是,Thread.sleep() 方法不是真正意义上的线程等待,它只是让线程暂停执行一段时间,不会释放锁或资源。在实际开发中,应根据具体需求选择合适的线程等待机制,以确保程序的正确性和效率。

代码如下:

public class demo6 {
    public static void main(String[] args) {

        Thread thread = new Thread(()->{

            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("正在执行 thread 线程");
            }
        });
        thread.start();
        
    }
}

运行结果:

        每相隔 1 秒就会输出一次。

        需要注意的是,这里的 Thread.sleep() 方法是受查异常,且 run() 是重写父类的方法,该 run() 方法的方法名、参数列表、声明异常都需要与父类保持一致,因此这里不能声明异常,只能捕获异常处理。

        2.0 线程状态

        在 Java 中主要包括六种不同的状态。

        2.1 新建状态 - NEW

        当创建一个线程对象时,线程处于新建状态,此时线程对象已经创建好了,但是还没调用 start() 方法启动线程,因此线程还没被创建出来

        2.2 就绪状态 - Runnable

        有两种情况都属于就绪状态:1)还没运行,就绪状态。但是线程已经准备好运行了,只等待被 CPU 调度执行。2)线程正在被 CPU 调度执行中,运行状态。总而言之,无论是就绪状态还是运行状态在 Java 中都属于 Runnable 状态,即就绪状态。

        2.3 终止状态 -Terminated

        线程执行完任务后或者出现异常导致线程终止时,线程进入终止状态。在终止状态下,线程不再执行任务。

        2.4 等待状态 - Waiting

        线程进入等待状态通常时因为调用了 thread.join() 方法等等。

代码如下:

public class demo7 {
    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(()->{
            while (true){
                System.out.println(1);
            }
        });
        thread.start();

        thread.join();
        System.out.println("正在执行 main 线程");
    }
}

演示线程等待:

        main 线程调用了 thread.join() 方法阻塞等待 thread 线程,又因为 thread 还当前为止还没结束, 所以当前 main 线程被阻塞了,因此 main 状态为 Waiting 状态。对于 thread 线程来说,目前的状态为 Runnable 状态。

        2.5 超时等待状态 - Time_Waiting

        线程调用带有超时参数的等待方法,比如 Thread.sleep(long millis) 等待方法。线程会进入超时等待状态下,线程会等待一段时间后自动恢复到就绪状态。

代码如下:

public class demo8 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            try {
                Thread.sleep(9000000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("正在执行 thread 线程");
        });
        thread.start();

        System.out.println("正在执行 main 线程");
    }
}

演示超时等待:

        2.6 阻塞状态 - Blocked

        线程在特定情况下,会进入阻塞状态,比如调用了 Thread.sleep() 方法或者加锁。在线程阻塞状态下,线程暂时停止执行,直到满足特定条件后,才能继续执行。

代码如下:

死锁状态:两个线程两把锁

public class demo10 {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        Thread t1 = new Thread(()->{
           synchronized (o1){
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               synchronized (o2){
                   System.out.println("正在执行 t1 线程");
               }
           }
        });


        Thread t2 = new Thread(()->{
           synchronized (o2){

               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               synchronized (o1){
                   System.out.println("正在执行 t2 线程");
               }
           }
        });

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

演示阻塞状态:

        2.7 线程状态之间的相互转换图

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

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

相关文章

微服务(基础篇-004-Feign)

目录 http客户端Feign Feign替代RestTemplate&#xff08;1&#xff09; Feign的介绍&#xff08;1.1&#xff09; 使用Feign的步骤&#xff08;1.2&#xff09; 自定义配置&#xff08;2&#xff09; 配置Feign日志的两种方式&#xff08;2.1&#xff09; Feign使用优化…

订单系统-RPC快速入门

RPC快速入门 概述 关于rpc&#xff0c;只需要知道他是一种协议&#xff0c;项目之间能够远程调用函数。 快速入门 我们前边下载好的两个包&#xff0c;在idea中打开之后&#xff0c;我们创建这么几个文件夹。 至于是干什么的&#xff0c;以后细说。创建好之后我们在produc…

buy me a btc 使用数字货币进行打赏赞助

最近在调研使用加密货币打赏的平台&#xff0c;发现idatariver平台 https://idatariver.com 推出的buymeabtc功能刚好符合使用场景&#xff0c;下图为平台的演示项目, 演示项目入口 https://buymeabtc.com/idatariver 特点 不少人都听说过buymeacoffee&#xff0c;可以在上面发…

Linux 学习之路--工具篇--yum

前面介绍了权限有关的内容&#xff0c;这里继续介绍有关Linux里面常用的工具之一yum 目录 一、简单介绍 <1> 源代码安装 <2>rpm 包安装 <3>yum / apt-get(ubuntu) 安装 二、简单使用 <1>安装包介绍 <2> yum 的基本指令 -- install <…

【C++程序员的自我修炼】基础语法篇(一)

心中若有桃花源 何处不是水云间 目录 命名空间 &#x1f49e;命名空间的定义 &#x1f49e; 命名空间的使用 输入输出流 缺省参数 函数的引用 引用的定义&#x1f49e; 引用的表示&#x1f49e; 引用的特性&#x1f49e; 常量引用&#x1f49e; 引用的使用场景 做参数 做返回值…

蓝桥杯基础练习汇总详细解析(一)——数列排序、十六进制转八进制、十六进制转十进制、十进制转十六进制、特殊回文数(代码实现、解题思路、Python)

试题 基础练习 数列排序 资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 给定一个长度为n的数列&#xff0c;将这个数列按从小到大的顺序排列。1<n<200 输入格式 第…

ATFX汇市:欧元区的2月M1增速为-7.7%,潜在通胀下修,欧元币值受冲击

ATFX汇市&#xff1a;衡量经济体的潜在通胀指标&#xff0c;除了CPI数据、失业率数据外&#xff0c;还有M1、M3数据。昨日&#xff0c;欧洲央行公布了2月份欧元区货币发展报告&#xff0c;其中提到&#xff1a;广义货币总量M3的年增长率从1月份的0.1%上升到2024年2月的0.4%&…

MT9256 Android 智能电视解决方案

一、方案描述 智能电视&#xff0c;是基于Internet应用技术&#xff0c;具备开放式操作系统与芯片&#xff0c;拥有开放式应用平台&#xff0c;可实现双向人机交互功能&#xff0c;集影音、娱乐、数据等多种功能于一体&#xff0c;以满足用户多样化和个性化需求的电视产品。有…

JavaScript高级 —— 学习(一)

目录 一、作用域 &#xff08;一&#xff09;局部作用域 1.函数作用域 2.块作用域 &#xff08;二&#xff09;全局作用域 二、垃圾回收机制 GC &#xff08;一&#xff09;生命周期 1.内存分配 2.内存使用 3.内存回收 4.特殊情况——内存泄漏&#xff1a; 注意&…

【问题分析】InputDispatcher无焦点窗口ANR问题【Android 14】

1 问题描述 Monkey跑出的无焦点窗口的ANR问题。 特点&#xff1a; 1&#xff09;、上层WMS有焦点窗口&#xff0c;为Launcher。 2&#xff09;、native层InputDispacher无焦点窗口&#xff0c;上层为”recents_animation_input_consumer“请求了焦点&#xff0c;但是”rece…

亚信安慧AntDB引领优质解决方案

亚信安慧AntDB数据库在运营商自主可控替换项目中的成功应用&#xff0c;具有极其重要的意义。该数据库的落地&#xff0c;不仅为这一项目注入了强大的支持力量&#xff0c;还在更大程度上提升了整体的运营效能。作为一种高效可靠的数据库解决方案&#xff0c;AntDB引入了先进的…

C++从入门到精通——函数重载

函数重载 前言一、函数重载概念二、函数重载的分类参数类型不同的函数重载参数个数不同的函数重载参数类型顺序不同的函数重载 三、函数重载的具体代码展示main.cpp 四、为什么为什么C支持函数重载&#xff0c;而C语言不支持函数重载呢 前言 函数重载是指在同一个作用域内&…

vue实现文字一个字一个字的显示(开箱即用)

图示&#xff1a; 核心代码 Vue.prototype.$showHtml function (str, haveCallback null) {let timeFlag let abcStr for (let i 0; i < str.length; i) {(function (i) {timeFlag setTimeout(function () {abcStr str[i]haveCallback(abcStr)if ((i 1) str.length…

SI24R2E:智能电子学生卡2.4GHz考勤方案

今年年初教育部发布的《关于加强中小学生手机管理工作的通知》中提出&#xff0c;学生手机有限带入校园&#xff0c;原则上不得将个人手机带入校园&#xff0c;禁止带入课堂&#xff1b;应设立校内公共电话、建立班主任沟通热线、探索使用具备通话功能的电子学生证或提供其他家…

List操作add,clear,addall报错UnsupportedOperationException的解决办法

ArrayList和Arrays.ArrayList是两码事 ArrayList 支持 add&#xff0c;clear&#xff0c;addall Arrays.ArrayList不支持add&#xff0c;clear&#xff0c;addall 这个方法的使用时候&#xff0c;传递的数组必须是对象数组&#xff0c;而不是基本数据类型 JDK源码 /** *返回由…

LLM之RAG实战(三十六)| 使用LangChain实现多模态RAG

我们之前介绍的RAG&#xff0c;更多的是使用输入text来查询相关文档。在某些情况下&#xff0c;信息可以出现在图像或者表格中&#xff0c;然而&#xff0c;之前的RAG则无法检测到其中的内容。 针对上述情况&#xff0c;我们可以使用多模态大模型来解决&#xff0c;比如GPT-4-V…

【微服务】Sentinel(流量控制)

文章目录 1.基本介绍1.Sentinel是什么2.Sentinel主要特性3.Sentinel核心功能1.流量控制2.熔断降级3.消息削峰填谷 4.Sentinel两个组成部分 2.Sentinel控制台显示1.需求分析2.下载3.运行1.进入cmd2.输入java -jar sentinel-dashboard-1.8.0.jar3.查看默认端口8080 4.访问1.账号和…

数据结构课设-基于Python的校园导航系统(附源码)

一月份的数据结构课设完成后&#xff0c;我对Python的了解也更加深刻。现将课设报告及源码开源&#xff0c;不足之处希望大家指正。源码我放在博客主页的资源中&#xff0c;需要的话大家自行下载&#xff08;用户信息保存在 users.json 文件中&#xff0c;地图信息保存在 campu…

GooglePlay无法下载应用问题

问题如下 解决方法 1、实际上是因为google尚未添加apk downloader扩展程序 2、添加该扩展程序后&#xff0c;在应用中搜索应用名即可 欧克&#xff01;下载完成

IDEA设置内存大小不生效

IDEA设置内存大小不生效 100%可行的方法 -Xms512m -Xmx4096m1.首先要找对idea加载的是哪个配置文件。 2.找到idea启动文件夹&#xff0c;编辑idea.bat 添加打印修改文件路径的代码&#xff0c;运行idea.bat打印一下你的配置文件路径&#xff0c;找到路径 修改 然后运行idea.…
最新文章