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

news2024/11/26 16:54:27

🔥博客主页: 【小扳_-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.coloradmin.cn/o/1554049.html

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

相关文章

解决 linux 服务器 java 命令不生效问题

在Linux系统中&#xff0c;当你安装Java并设置了JAVA_HOME环境变量后&#xff0c;你可能需要使用source /etc/profile命令来使Java命令生效。这是因为/etc/profile是一个系统级的配置文件&#xff0c;它包含了系统的全局环境变量设置。 但是需要注意的是&#xff0c;source /e…

防水游泳耳机有哪些?四款口碑销量双丰收的尖货揭晓

游泳时享受音乐的快乐已成为许多人的追求。防水游泳耳机&#xff0c;作为这一追求的重要工具&#xff0c;不仅让我们在水中畅游时能够感受到音乐的魅力&#xff0c;同时也提升了游泳的乐趣。然而&#xff0c;面对市场上琳琅满目的产品&#xff0c;如何选择一款既防水又音质出色…

Java基础-子类与继承

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、继承的概念 二、Java 方法重写 三、Object类 方法 四、final关键字 final 变量 五、Java 多态 …

Oracle 19C RAC集群补丁升级

文章目录 一、补丁包概述二、OPatch检查和更新Grid用户更新OPatchOracle用户更新OPatch 三、验证Oracle Inventory的有效性四、运行 OPatch 冲突检查五、运行opatch命令检查GI HOME下是否有足够的空间六、补丁冲突检测与解决&#xff08;修补程序&#xff09;七、使用root用户应…

scGRN:人与鼠的GRN平台

基因调控网络GRN是包含转录因子TFs与其下游靶基因之间的调控相互作用的可解释图模型。了解GRN的拓扑结构和动力学是解释疾病病因机制和将相应发现转化为新疗法的基础。单细胞多组学技术的最新进展促使从单细胞转录组学和表观基因组学数据中以前所未有的分辨率推断GRN。在这里&a…

mysql5.7 源码分析--初始化

集中在sql\mysqld.cc文件的mysqld_main函数中&#xff08;&#xff09;&#xff1a; 主程序入口 在sql\main.cc文件中&#xff1a; int main(int argc, char **argv) {return mysqld_main(arg, argv); } 一、mysql为了跨平台&#xff0c;对win32系统做了单独的初始化&#x…

GitLab更新失败(Ubuntu)

在Ubuntu下使用apt更新gitlab报错如下&#xff1a; An error occurred during the signature verification.The repository is not updated and the previous index files will be used.GPG error: ... Failed to fetch https://packages.gitlab.com/gitlab/gitlab-ee/ubuntu/d…

双指针算法_和为 s 的两个数_C++

题目&#xff1a; 输入一个递增排序的数组&#xff0c;和一个数字s&#xff0c;在数组中查找两个数&#xff0c;使得它们的和正好是数字s&#xff0c;如果有多对数字的和等于s&#xff0c;只需要任意输入一对即可 算法原理&#xff1a; 利用单调性&#xff01;使用双指针算法&…

STL —— vector(1)

博主首页&#xff1a; 有趣的中国人 专栏首页&#xff1a; C专栏 本篇文章主要讲解vector使用的相关内容 1. vector简介 vector 是 C 标准库中的一个容器类模板&#xff0c;它提供了动态数组的功能&#xff0c;可以方便地管理和操作元素的集合。下面是关于 vector 的一些基本信…

基于unbantu的nginx的配置

目录 前言: 1.安装nginx并进行测试 1.1使用nginx -v 命令查看版本 1.2开启服务 查看端口 1.3测试 2.nginx的静态资源访问配置 2.1创建静态资源存放的目录 2.2写入目录中测试文件对应的内容 2.3修改配置文件 2.4 测试 3.虚拟主机配置 3.1创建目录 3.2写入测试…

[flask]http请求//获取请求体数据

import jsonfrom flask import Flask, requestapp Flask(__name__)app.route("/form1", methods["post"]) def form1():"""获取客户端请求的请求体[表单]:return:""""""获取表单数据请求url&#xff1a;&qu…

【mysql】centos7安装mysql8

目录 1. 下载安装包2. 解压tar包3. 安装4. 查看安装完成后的安装包5. 初始化mysql6. MySQL设置 1. 下载安装包 1.官网地址2.mysql-8.0.35-1.el7.x86_64.rpm-bundle.tar3.放到服务器的目录&#xff08;这里是/opt/mysql&#xff09; 2. 解压tar包 1.tar -xvf 可以解压tar.xz后…

批量下载网页文章软件大全

在信息爆炸的时代&#xff0c;网络上充斥着海量的文章和内容&#xff0c;有时我们希望能够批量下载网页上的文章&#xff0c;以便离线阅读或进行进一步处理。为了满足这一需求&#xff0c;出现了许多网页文章批量下载工具&#xff0c;它们能够帮助用户快速、方便地获取网页上的…

【IC前端虚拟项目】write_path子模块DS与RTL编码

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 read_path的代码完成之后,就可以开始整个项目里复杂度最高、bug最多、时序收敛最为困难的模块——write_path的开发了!我自己写过两次这个虚拟项目,每次都是在这里耗时最久,所以大家也可以挑战一下自…

使用AOP实现打印日志

首先创建annotation.SystemLog类&#xff1a; package com.gjh.annotation;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;Target(ElementType.METHOD…

蓝桥杯-卡片换位

solution 有一个测试点没有空格&#xff0c;要特别处理&#xff0c;否则会有一个测试点运行错误&#xff01; 还有输入数据的规模在变&#xff0c;小心顺手敲错了边界条件 #include<iostream> #include<string> #include<queue> #include<map> #incl…

JS new Array.fill(new Array()) 创建二维数组 fill方法的坑

我们通常会通过如下方式来创建一个二维数据&#xff1a; const arr new Array(5).fill(new Array(2).fill(0))我们如果想要修改其中一个元素的值 arr[0][0] 1输出&#xff1a;   我们只想给arr[0][0]赋值&#xff0c;但是每一行数组为0的下标元素的值全部改变了&#xf…

Mysql-实战数据备份与恢复

文章目录 备份类型备份内容备份工具mysqldump备份 实战案例&#xff1a;恢复误删除的表准备工作2:30完全备份完全备份后更新数据表10:00误删students表需要恢复还原的状态开始还原恢复 为什么要备份&#xff1f; 备份是为了&#xff1a;灾难恢复&#xff1a;硬件故障、软件故障…

基于springboot实现房屋租赁系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现房屋租赁系统演示 摘要 社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。互联网具有便利性&#xff0c;速度快&#xff0c;效率高&#xff0c;成本…

转型数据报表开发:奥威BI学习网站如何助你一臂之力?

介绍&#xff1a;奥威BI是一款商业智能工具&#xff0c;专为中国企业设计&#xff0c;提供数据可视化分析功能。以下是关于奥威BI的一些详细信息&#xff1a; 自助式敏捷BI分析&#xff1a;用户可以通过简单的操作&#xff0c;如拖拉拽&#xff0c;制作出炫酷的可视化报表。这种…