线程的六种状态

news2024/9/20 22:30:39

优质博文:IT-BLOG-CN

线程的状态在Thread.State这个枚举类型中定义:共有6种状态,可以调用线程Thread种的getState()方法获取当前线程状态。

public enum State   
{  
       /** 
        * 新建状态(New): 
        * 当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
        */  
       NEW,  
         
       /** 
        * 就绪状态(Runnable)
        * 一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。
        * 当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。
        * 当start()方法返回后,线程就处于就绪状态。
        */  
       RUNNABLE,  

       /**
        * 运行状态(Running)
        * 当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
        */
        RUNNING,

       /** 
        * 阻塞状态(Blocked)
        * 线程运行过程中,可能由于各种原因进入阻塞状态:
        * 1>线程通过调用sleep方法进入睡眠状态;
        * 2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
        * 3>线程试图得到一个锁,而该锁正被其他线程持有;
        * 4>线程在等待某个触发条件;
        * ......
        *  所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
        */  
       BLOCKED,  
     
       /** 
        * 一个正在等待的线程的状态。也称之为等待状态。
        * 造成线程等待的原因有三种,分别是调用Object.wait()、join()以及LockSupport.park()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。
        * 例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。
        */  
       WAITING,  
         
       /** 
        * 一个在限定时间内等待的线程的状态。也称之为限时等待状态。
        * 造成线程限时等待状态的原因有五种,分别是:
        *   Thread.sleep(long)、Object.wait(long)、join(long)、LockSupport.parkNanos(obj,long)和LockSupport.parkUntil(obj,long)。
        */  
       TIMED_WAITING,  
  
       /** 
        * 一个完全运行完成的线程的状态。也称之为终止状态、结束状态。
        */  
       TERMINATED;  
}

一、六种状态转换图

操作系统中,线程被视为轻量级的进程,所以线程状态就是进程的状态,一个线程确实可以被视为一个轻量级进程Lightweight Process, LWP

二、新建状态(NEW)

new关键字新建一个线程,这个线程就处于新建状态。

每一个线程,在堆内存中都有一个对应的Thread对象。Thread t = new Thread();当刚刚在堆内存中创建Thread对象,还没有调用t.start()方法之前,线程就处在NEW状态。在这个状态上,线程与普通的java对象没有什么区别,就仅仅是一个堆内存中的对象。例如:

private void testStateNew() {
    Thread thread = new Thread(() -> {});
    System.out.println(thread.getState()); // 输出 NEW
}

三、运行状态(RUNNABLE)

操作系统中的READY(就绪状态)和RUNNING(运行状态)两种状态,在Java中统称为RUNNABLE

READY(就绪状态)

当线程对象调用了start()方法之后,线程处于就绪状态,就绪意味着该线程可以执行,但具体啥时候执行将取决于JVM里线程调度器的调度。

【1】不允许对一个线程多次使用start:通过下面源码分析得知,在start()内部,有一个threadStatus变量。如果它不等于0,调用start()会直接抛出异常。

// 使用synchronized关键字保证这个方法是线程安全的
public synchronized void start() {
    // threadStatus != 0 表示这个线程已经被启动过或已经结束了
    // 如果试图再次启动这个线程,就会抛出IllegalThreadStateException异常
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    // 将这个线程添加到当前线程的线程组中
    group.add(this);

    // 声明一个变量,用于记录线程是否启动成功
    boolean started = false;
    try {
        // 使用native方法启动这个线程
        start0();
        // 如果没有抛出异常,那么started被设为true,表示线程启动成功
        started = true;
    } finally {
        // 在finally语句块中,无论try语句块中的代码是否抛出异常,都会执行
        try {
            // 如果线程没有启动成功,就从线程组中移除这个线程
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            // 如果在移除线程的过程中发生了异常,我们选择忽略这个异常
        }
    }
}

我们在看下ThreadStatus的取值逻辑:如下源码可知,调用start()之后线程的状态就!0了。

// Thread.getState方法源码:
public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}

// sun.misc.VM 源码:
// 如果线程的状态值和4做位与操作结果不为0,线程处于RUNNABLE状态。
// 如果线程的状态值和1024做位与操作结果不为0,线程处于BLOCKED状态。
// 如果线程的状态值和16做位与操作结果不为0,线程处于WAITING状态。
// 如果线程的状态值和32做位与操作结果不为0,线程处于TIMED_WAITING状态。
// 如果线程的状态值和2做位与操作结果不为0,线程处于TERMINATED状态。
// 最后,如果线程的状态值和1做位与操作结果为0,线程处于NEW状态,否则线程处于RUNNABLE状态。
public static State toThreadState(int var0) {
    if ((var0 & 4) != 0) {
        return State.RUNNABLE;
    } else if ((var0 & 1024) != 0) {
        return State.BLOCKED;
    } else if ((var0 & 16) != 0) {
        return State.WAITING;
    } else if ((var0 & 32) != 0) {
        return State.TIMED_WAITING;
    } else if ((var0 & 2) != 0) {
        return State.TERMINATED;
    } else {
        return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
    }
}

【2】其他状态转换为就绪状态:
  ☑️ 线程调用start(),新建状态转化为就绪状态。
  ☑️ 线程sleep(long)时间到,等待状态转化为就绪状态。
  ☑️ 阻塞式IO操作结果返回,线程变为就绪状态。
  ☑️ 其他线程调用join()方法,结束之后转化为就绪状态。
  ☑️ 线程对象拿到对象锁之后,也会进入就绪状态。

RUNNING(运行状态)

处于就绪状态的线程获得了CPU之后,真正开始执行run()方法的线程执行体时,意味着该线程就已经处于运行状态。需要注意的是,对于单处理器,一个时刻只能有一个线程处于运行状态。对于抢占式策略的系统来说,系统会给每个线程一小段时间处理各自的任务。时间用完之后,系统负责夺回线程占用的资源。下一段时间里,系统会根据一定规则,再次进行调度。

运行状态转变为就绪状态的情形:
  ☑️ 线程失去处理器资源。线程不一定完整执行的,执行到一半,说不定就被别的线程抢走了。
  ☑️ 调用yield()静态方法,提示调度程序,当前线程愿意放弃当前对处理器的使用。这时,当前线程将会被置为就绪状态,和其他线程一样等待调度,这时候根据不同优先级决定的概率,当前线程完全有可能再次抢到处理器资源。

如果线程长时间停留在在这个状态就不正常了,这说明线程运行的时间很长(存在性能问题),或者是线程一直得不得执行的机会(存在线程饥饿的问题)。

四、阻塞状态(BLOCKED)

线程正在等待获取java对象的监视器(也叫内置锁),即线程正在等待进入由synchronized保护的方法或者代码块。synchronized用来保证原子性,任意时刻最多只能由一个线程进入该临界区域,其他线程只能排队等待。

线程取得锁,就会从阻塞状态转变为就绪状态。

五、等待状态(WAITING)

进入该状态表示当前线程需要等待其他线程做出一些的特定的动作(通知或中断)。也就是说,如果不发生特定的事件,那么处在该状态的线程一直等待,不能获取执行的机会。比如:A线程调用了obj对象的obj.wait()方法,如果没有线程调用obj.notifyobj.notifyAll,那么A线程就没有办法恢复运行; 如果A线程调用了LockSupport.park(),没有别的线程调用LockSupport.unpark(A),那么A没有办法恢复运行。

六、超时等待状态(TIMED_WAITING)

区别于WAITING,它可以在指定的时间自行返回。

J.U.C中很多与线程相关类,都提供了限时版本和不限时版本的APITIMED_WAITING意味着线程调用了限时版本的API,正在等待时间流逝。当等待时间过去后,线程一样可以恢复运行。如果线程进入了WAITING状态,一定要特定的事件发生才能恢复运行;而处在TIMED_WAITING的线程,如果特定的事件发生或者是时间流逝完毕,都会恢复运行。

运行状态转换为超时等待:
  ☑️ 调用静态方法Thread.sleep(long)
  ☑️ 线程对象调用wait(long)方法
  ☑️ 其他线程调用指定时间的join(long)
  ☑️ LockSupport.parkNanos()
  ☑️ LockSupport.parkUntil()

七、消亡状态

线程执行完毕run方法或call方法,或者抛出没有捕获的ExceptionError异常而结束,线程都会停留在这个状态。这个时候线程只剩下Thread对象了,没有什么用了。

即线程的终止,表示线程已经执行完毕。前面已经说了,已经消亡的线程不能通过start再次唤醒。

需要注意的是:主线成和子线程互不影响,子线程并不会因为主线程结束就结束。

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

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

相关文章

C++第五节 - this指针、构造函数、析构函数

一、类对象的存储方式 只保存成员变量&#xff0c;成员函数存放在公共的代码段 注意点&#xff1a; #include<iostream> using namespace std; class A { public:void PrintA(){cout << _a << endl;} private:char _a; }; int main() {class A a;cout <…

CSS“多列布局”(补充)——WEB开发系列35

多列布局是一种非常常见的布局方式&#xff0c;适用于内容丰富的页面&#xff0c;如新闻网站、杂志或博客。 一、CSS多列布局概述 CSS多列布局允许我们将内容分成多个垂直列&#xff0c;使页面布局更加灵活和多样化。多列布局的主要属性包括 ​​column-count​​、​​column…

django学习入门系列之第十点《A 案例: 员工管理系统4》

文章目录 6 部门管理&#xff08;原始方式&#xff09;6.6 添加界面的导入&#xff08;数据库&#xff09;6.7 删除按键的应用6.8 编辑按键的应用6.81 传值的另一种方式 6.9 提交按键的应用 往期回顾 6 部门管理&#xff08;原始方式&#xff09; 6.6 添加界面的导入&#xff…

E32.【C语言 】练习:蓝桥杯题 懒羊羊字符串

1.题目 【问题描述】 “懒羊羊”字符串是一种特定类型的字符串&#xff0c;它由三个字符组成&#xff0c;具有以下特点: 1.字符串长度为 3. 2.包含两种不同的字母。 3.第二个字符和第三个字符相同 换句话说&#xff0c;“懒羊羊”字符串的形式应为 ABB&#xff0c;其中A和B是不…

【python2C】算法基础:计时比较

不断改进代码&#xff0c;是学习进步必经之路。 判断代码优劣&#xff0c;在空间允许的情况下&#xff0c;计时就是最可靠的标尺。 打表不算&#xff0c;人脑不算 1.对于答案较为固定的题&#xff0c;预先算出所有可能的答案表&#xff0c;然后对应输入查找答案&#xff0c;从…

【OpenGL 002】着色器 GLSL 语言及GLFW代码案例

文章目录 1.GLSL语言简介本节案例 code 1 2.GLSL的数据类型① 向量(Vector)- 向量重组示例- 向量重组禁忌 3.GLSL的输入输出本节案例 code 2 4.着色器示例5.Uniform本节案例 code 3 通过第一节 【OpenGL 001】Ubuntu 搭建 GLFW 环境及其相关测试 demo 想必已经搭建好了GLFW环境…

『功能项目』GameObject对象池 - 第三职业【39】

本章项目成果展示 我们打开上一篇38管理器基类的项目&#xff0c; 本章要做的事情是利用对象池制作第三个职业——魔法师 在GameRoot对象下创建空物体 重命名为PoolRoot 将GameRoot拖拽至预制体 创建脚本&#xff1a; 编写脚本&#xff1a;PoolManager.cs using UnityEngine;…

使用豆包MarsCode 编写 Node.js 全栈应用开发实践

以下是「豆包MarsCode 体验官」优秀文章&#xff0c;作者狼叔。 欢迎更多用户使用豆包MarsCode 并分享您的产品使用心得及反馈、创意项目开发等&#xff0c;【有奖征集&#xff5c;人人都是豆包MarsCode 测评官&#xff01;】活动正在火热进行中&#xff0c;欢迎大家投稿参加&a…

【Python报错已解决】 SyntaxError: invalid syntax

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一&#xff1a;修复缺失的括号或引号2.…

甘肃五仁月饼:传统风味,舌尖上的乡愁

在中秋佳节的美食画卷中&#xff0c;甘肃食家巷五仁月饼以其独特的魅力占据着重要的一席之地。甘肃五仁月饼&#xff0c;那朴实无华的外表下&#xff0c;藏着无尽的美味与情怀。它的饼皮金黄酥脆&#xff0c;散发着淡淡的麦香&#xff0c;仿佛在诉说着古老的制作工艺。轻轻咬上…

Java:动态代理

Java&#xff1a;动态代理 什么是代理 代理模式 是一种设计模式&#xff0c;它为其他对象提供了一种代理以控制对这个对象的访问。代理对象通常包装实际的目标对象&#xff0c;以提供一些附加的功能&#xff08;如延迟加载、访问控制、日志记录等&#xff09;。我们一般可以使…

C++中的内存管理和模板初识

一、内存管理 1.1内存区域的划分 1.1.1内存划分区域图示 1.1.1补&#xff1a;堆和栈都可以进行动态分配和静态分配吗&#xff1f; 不是的&#xff0c;堆无法进行静态分配&#xff0c;只能动态分配&#xff1b;栈可以利用_alloca动态分配&#xff0c;但是分配的空间不能用fre…

基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,系统包含GUI操作界面&#xff0c;系统支持对文字,灰度图,彩色图,语音进行加解密。 2.测试软件版本以及…

项目运行插件-日志管理

日志管理 项目运行时模块提供了项目日志收集&#xff0c;检索和保存查询方案等功能。 体验地址&#xff1a; http://119.163.197.219:13456/view/runtime/index.html#/log/aioLogPage 沟通加QQ群 &#xff1a; 908377977 gitee 开源地址 &#xff1a; https://gitee.com/aio…

打印文档时,只有图片中的文本不清晰该如何处理

最近打印东西的时候&#xff0c;发现只有图片中的文本并不清晰&#xff0c;就想研究一下如何改善这个问题。 打印机是佳能的 MF113w&#xff0c;一个不错的多功能激光黑白打印机&#xff0c;支持无线打印。唯一问题就是每次 DHCP 分配 IP 到期后&#xff0c;这款打印机就会亮错…

AI提质增效率赋能工业产品质检,基于高精度YOLOv5全系列参数【n/s/m/l/x】模型开发构建工业生产场景下PCB电路板缺陷问题智能化分割检测识别分析系统

在PCB电路板的生产制造过程中&#xff0c;质量检测是确保产品质量、维护品牌形象的关键环节。然而&#xff0c;传统的人工检测方式依赖于经验丰富的工人师傅通过光学显微镜等设备进行逐块检查&#xff0c;这不仅劳动强度大、效率低下&#xff0c;而且受限于人的主观判断、视力疲…

《华为 eNSP 模拟器安装教程》

1.电脑安装环境要求&#xff1a; 检查电脑是否安装过 eNSP 和依赖软件&#xff0c;如果有&#xff0c;请全部卸载。 安装软件列表&#xff1a; 2.软件安装&#xff1a; 安装 WinPcap&#xff1a; 打开安装包&#xff0c;单击【Next】 单击【I Agree】 单击【Install】 单击【…

《信息系统安全》课程实验指导

第1关&#xff1a;实验一&#xff1a;古典密码算法---代换技术 任务描述 本关任务&#xff1a;了解古典密码体制技术中的代换技术&#xff0c;并编程实现代换密码的加解密功能。 注意所有明文字符为26个小写字母&#xff0c;也就是说字母表为26个小写字母。 相关知识 为了完…

1、常用的数据库、表操作

基本的建表和数据库拷贝操作。 一、数据定义语言DDL show databases; # 查看全部数据库 show create database db; # 查看数据库db create database db; # 创建数据库db drop database db; # 删除数据库db use db; # 使用数据库db基本…

1 Linux SSH安全加固_linux system-auth

![在这里插入图片描述](https://img-blog.csdnimg.cn/20201117150524918.png?x-oss-processimage/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwOTA3OTc3,size_16,color_FFFFFF,t_70#pic_center) ![在这里插入图片描述](https://…