终于搞懂了线程的状态以及状态转换

news2025/1/13 15:51:33

1 线程的状态

线程的状态是一个枚举类型 Thread.State
public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
        }
    }
}

  1. 新建(New)

    意义:线程刚刚被创建但尚未开始执行。在这个状态下,线程对象已经被创建,但还没有调用其start()方法来启动线程执行。线程此时不会占用系统资源,只是处于等待被启动的状态。
  2. 就绪(Runnable)

    意义:线程是可工作的。又可以分成正在工作中和即将开始工作,但是没有明确的区分,两种情况下都称之为Runnable。
  3. 阻塞(Blocked)

    意义:线程被阻塞,暂时无法继续执行。这可能是因为线程在等待某个资源(如锁、IO操作、等待其他线程的通知等)而被挂起,无法继续执行任务。
  4. 等待(Waiting)

    意义:线程进入等待状态,等待特定条件的满足。线程会等待,直到其他线程显式地唤醒它或者等待的条件变为真。
  5. 超时等待(Timed Waiting)

    意义:线程进入有限时间的等待状态。与等待状态类似,但可以设置一个超时时间,线程会等待一段时间后自动唤醒。

2 状态转换

2.1NEW RUNNABLE TERMINATED 状态的转换

public class ThreadStateTransfer {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
            }
        }, "thread-1");
        System.out.println(t.getName() + ": " + t.getState());;
        t.start();
        while (t.isAlive()) {
            System.out.println(t.getName() + ": " + t.getState());;
        }
        System.out.println(t.getName() + ": " + t.getState());;
    }
}

从代码中我们可以清楚的看到线程thread-1从创建到执行完毕自己的“任务”的整个状态转换,NEW-RUNNABLE-TERMINATED。

2.2 WAITING 、 BLOCKED 、 TIMED_WAITING 状态

 首先看一下TIME_WAITING和BOLCKED状态,通过代码的方式来认识一下这两个状态的含义。

public class ThreadStateTransfer2 {
    public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    while (true) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("hehe");
                }
            }
        }, "t2");
        t2.start();
    }
}

 在这个代码中线程t1首先获取到锁,然后自己会一直进入休眠(休眠是带有时间上限的,但是又与while的条件实际上是无休止的休眠,单独对于一次休眠等待来说是有时间上限的。)线程t2此时创建并启动但是由于获取不到锁对象,因此进入阻塞状态等待线程t1释放锁对象

使用jconsole工具来查看当前两个线程的状态:

那么WAITING状态又是怎么样子的呢?

只需要将上面代码中的t1中的sleep换成wait即可:

public class ThreadStateTransfer2 {
    public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                        try {
                            //修改这个sleep
                            //Thread.sleep(1000);
                            object.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("hehe");
                }
            }
        }, "t2");
        t2.start();
    }
}

此时线程t1同样是率先获得锁对象,但是不在进行“休眠操作”,而是直接进入等待状态同时放开自己拥有的锁对象,此时的等待是等待拥有此锁对象的其他线程来通知t1结束等待,可惜的是t1永远也等待不到通知(代码中没有写....)。然后t1再启动,这次t1不再进入阻塞状态,因为此时的锁对象被t1所获取到后又通过wait释放了。

通过jconsole来观察此时的t1的状态(waiting):

而此时t2由于获取到锁对象,进入正常的工作,t2的状态就是RUNNABLE然后会转换成TERMINATED.

那么t1如何从WAITING装换成RUNNABLE呢?只需要线程t2在结束自己的任务后,使用notify来通知t1线程即可。(就是谢谢你t1刚才等待我,让出锁,现在我t2事务工作执行完成了,你t1可以继续执行了)

public class ThreadStateTransfer2 {
    public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                        try {
                            //修改这个sleep
                            //Thread.sleep(1000);
                            System.out.println("t1 wait 之前");
                            object.wait();
                            System.out.println("t1 收到了通知");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("t2 工作打印,hehe");
                    object.notify();
                }
            }
        }, "t2");
        t2.start();
        System.out.println("t1 的状态:"+t1.getState());
        System.out.println("t2 的状态:"+t2.getState());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("t2 的状态:"+t2.getState());
    }
}

 可以看到t1的状态从WAITING到最终TERMINATED的一个变化。

2.3 yield()----大公无私,让出cpu

public class ThreadYield {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("张三");
                    // 先注释掉, 再放开
                    // Thread.yield();
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("李四");
                }
            }
        }, "t2");
        t2.start();
    }
}

运行以上代码,可以观察到打印出张三、李四的数量大概是五五开,两者同时抢占式执行。

但是使用了yield()之后张三的数量就会远远小于李四,也就是线程t1在yield之后会让出cpu,此时不改变线程的状态,t1的状态一直都是RUNNABLE但是t1回去就绪队列中重新排队。之前说的RUNNABLE包含两种情况,一种是就绪中(可以工作)只是在排队,还有一种就是在执行中。

3 总结

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

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

相关文章

安卓绘制原理之 MeasureCache优化了什么?

安卓绘制原理概览_油炸板蓝根的博客-CSDN博客 搜了一下&#xff0c;全网居然没有人提过 measureCache。 在前文中提到过&#xff0c;measure的时候&#xff0c;如果命中了 measureCache&#xff0c;会跳过 onMeasure&#xff0c;同时会设置 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOU…

Mysql树形表的两种查询方案(递归与自连接)

你有没有遇到过这样一种情况&#xff1a; 一张表就实现了一对多的关系&#xff0c;并且表中每一行数据都存在“爷爷-父亲-儿子-…”的联系&#xff0c;这也就是所谓的树形结构 对于这样的表很显然想要通过查询来实现价值绝对是不能只靠select * from table 来实现的&#xff0…

微服务·数据一致-事务与分布式事务

微服务数据一致-事务与分布式事务 概述 事务是计算机科学和数据库管理中的一个关键概念&#xff0c;用于确保数据的一致性和可靠想。事务管理是大多数应用程序和数据库系统中不可或缺的一部分。分布式事务扩展了事务的概念&#xff0c;用于多个分布式系统和服务的数据一致性管…

1、java基本语法

1.、标识符、关键字、注释 标识符&#xff1a;对类、对象、变量、方法、数组等起个名字。 合法的标识符&#xff1a; 由字母、数字、下划线“_”组成&#xff0c;并且首字符不能是数字。不能把java关键字和保留字作为标识符。标识符区分大小写字母。 理论上只要满足上面三个…

Vuex -访问(modules)模块中的 state mutations actions getters

文章目录 四大核心复习一、获取模块内的state数据1.目标&#xff1a;2.使用模块中的数据3.代码示例 二、获取模块内的getters数据1.目标&#xff1a;2.语法&#xff1a;3.代码演示 三、获取模块内的mutations方法1.目标&#xff1a;2.注意&#xff1a;3.调用方式&#xff1a;4.…

LRTA*(Learning-RTA*)

1、基本概念 LRTA* 算法是对RTA* 算法的改进&#xff0c;在RTA* 的原论文中&#xff0c;提到了&#xff1a; Unfortunately, while RTA* as described above is ideally suited to single problem solving trials, it must be modified to accommodate multi-trial learning.…

集成学习-树模型

可以分为三部分学习树模型&#xff1a; 基本树&#xff08;包括 ID3、C4.5、CART&#xff09;.Random Forest、Adaboost、GBDTXgboost 和 LightGBM。 基本树 选择特征的准则 ID3&#xff1a;信息增益max C4.5&#xff1a;信息增益比max CART&#xff1a;基尼指数min 优缺…

【uniapp关联unicloud,阿里云云服务空间】unicloud本地调试服务启动失败:当前项目未关联unicloud服务空间,请调整后重新运行,已解决

最近开发app项目&#xff0c;很多都是第一次上手 1、在Hbuider中运行项目&#xff0c;出现如下提示 2、项目根目录下已有uniCloud文件夹 3、如果云开发环境未创建&#xff0c;可以右击项目&#xff0c;选择创建uniCloud云开发环境 4、创建好的目录如下&#xff0c;index.js中…

图解三重积分的对称性

1.图解三重积分的对称性 关于三重积分详见&#xff1a;三重积分(Triple Integral) 三重积分的对称性原理与二重积分类似&#xff0c;关于二重积分的对称性详见&#xff1a;图解二重积分的对称性 被积函数 f ( x , y , z ) f(x,y,z) f(x,y,z)可以有不同的物理意义&#xff0c;…

查询设计之查询条件对结果的影响

在sql使用中&#xff0c;最常用的就是查询。 在实践过程中&#xff0c;丰富的查询场景也使得查询sql有多种写法&#xff0c;不同的写法会带来不同的效果 以下&#xff0c;就查询条件对结果的影响。 一、数据 二、需求 把查询 type A’的 user 以及 type ‘A’ 的 department…

网络爬虫的意义:连接信息世界的纽带

本文将探讨网络爬虫的意义及其在连接信息世界中的重要作用。网络爬虫作为一种自动化程序&#xff0c;通过收集和提取互联网上的数据&#xff0c;为搜索引擎、数据分析和机器学习等领域提供了宝贵的资源。同时&#xff0c;我们也将探讨网络爬虫的伦理和法律责任&#xff0c;以及…

5、DVWA——文件上传

文章目录 一、文件上传原理二、low2.1 源码分析2.2 通关步骤 三、medium3.1 源码分析3.2 通关思路 四、high4.1 源码分析4.2 通关思路 一、文件上传原理 文件上传漏洞一般指用户上传了一个可执行的脚本文件&#xff0c;并通过此脚本文件获得了执行服务器端命令的能力。文件上传…

C语言经典100例题(45)--学习使用register定义变量的方法

目录 题目 问题分析 代码 运行结果 题目 学习使用register定义变量的方法 问题分析 register是做声明的&#xff0c;为了提高效率。 register变量不能做取地址运算符&操作。 声明变量具有register储类型就要求编译器把变量存储在寄存器中&#xff0c;而不是像其他变量…

第4篇 vue的基本语法操作以及组件,声明周期,路由的操作

一 vue常用操作案例 1.1 事件渲染 1.数据渲染的方式&#xff1a;使用插值表达式{{}}进行数据渲染 2.数据渲染的方式&#xff1a;以使用 v-bind指令&#xff0c;它的简写的形式就是一个冒号&#xff08;:&#xff09;&#xff0c;v-bind 特性被称为指令。指令带有前缀 v- 代…

OLED透明屏交互技术:开创未来科技的新篇章

OLED透明屏交互技术作为一项前沿的科技创新&#xff0c;正在以其高透明度、触摸和手势交互等特点&#xff0c;引领着未来科技的发展。 不仅在智能手机、可穿戴设备和汽车行业有着广泛应用&#xff0c;还在广告和展示领域展现出巨大的潜力。 那么&#xff0c;尼伽在这篇文章中将…

无涯教程-JavaScript - DDB函数

描述 DDB函数使用双倍余额递减法或您指定的某些其他方法返回指定期间内资产的折旧。 语法 DDB (cost, salvage, life, period, [factor])争论 Argument描述Required/OptionalCostThe initial cost of the asset.RequiredSalvage 折旧结束时的价值(有时称为资产的残值)。 该…

三、lock类的编写与解析 —— TinyWebServer

lock类的编写与解析 —— TinyWebServer 一、前言 这个类的作用作者已经给了解释 —— “多线程同步&#xff0c;确保任一时刻只能有一个线程能进入关键代码段.” 对于这句话其实看了&#xff0c;会有似懂非懂的感觉&#xff0c;然后写代码的时候&#xff0c;就会完全不懂。其…

分享一个开发者和设计者的免费图标库

图标是现代界面设计的不可或缺的一部分&#xff0c;无论是移动应用、网页设计还是软件界面。然而&#xff0c;许多开发者和设计师都面临着一个共同的挑战&#xff1a;寻找高品质、开源且免费的矢量图标资源。在这个领域&#xff0c;Yesicon 脱颖而出&#xff0c;成为了一个备受…

【计算机网络】UDP数据包是如何在网络中传输的?

List item 创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多计算机网络知识专栏&#xff1a;计算机网络&#x1f525;…

【性能测试】中间件优化

1、Tomcat 优化连接数、线程池 打开tomcat安装目录\conf\server.xml文件&#xff0c;在server.xml中 有以下配置&#xff1a; tomcat HTTP/1.1 <Connector port"8080" protocol"HTTP/1.1" maxThreads"1000" acceptCount"1500" c…