【JavaEE】线程状态

news2024/11/14 21:41:12

目录

前言

一.线程状态图

二.线程状态

1.初始状态(NEW)

 2.运行状态(RUNNING)

 3.等待状态(WAITING)

4.超时等待(TIMED_WAITING)

5.阻塞状态(BLOCKED)

 6.终止状态(TERMINATED)

 三.线程状态间的转换

四.总结 


前言

线程状态及其状态转换是线程生命周期中的关键部分,下面我们就来讲解六种线程状态以及其如何转换。

一.线程状态图

线程状态其实是一种枚举类型Thread.State

class T_state{
    public static void main(String[] args) {
        for(Thread.State state:Thread.State.values()){
            System.out.println(state);
        }
    }
}

 

  • NEW(初始):创建了一个线程,但还没有通过start方法调用。可以理解为安排了工作,但还没有开始工作。
  • RUNNABLE(运行):又分为Ready(就绪状态)Running(运行中状态)

当线程对象被创建之后,若有线程(比如main线程)调用了该线程对象的start,那么该线程就会进入可运行线程池进行等待线程调用,获取CPU的使用权,此时就处于Ready(就绪状态),当获取到cpu使用权之后,就转变为Running(运行中状态)。

Ready可以理解为即将开始工作,而Running即正在工作中。

  • BLOCKED(阻塞):线程试图获取一个锁对象,但此时锁对象被其他线程所占有,那么线程就进入阻塞等待,当获取到锁,则进入Running状态。
  • WAITING(等待):线程处于等待状态,等待其他线程唤醒,可以通过notify或者notifyAll方法来唤醒。
  • TIMED_WAITING(超时等待):可以在指定的时间内返回。可以使用带参数的sleep或者wait。
  • TERMINATED(终止):线程执行结束。主线程和子线程互不影响,子线程并不会因为主线程结束就结束。

二.线程状态

1.初始状态(NEW)

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

public static void main(String[] args) {
        // 创建一个线程,该线程在执行时会暂停1000毫秒
        Thread t=new Thread(()->{
            try {
                Thread.sleep(1000); // 模拟线程执行的延迟
            } catch (InterruptedException e) {
                e.printStackTrace(); // 处理线程被中断的异常
            }
        });
        System.out.println("t的线程状态:"+t.getState()); // 输出线程的当前状态
    }
t的线程状态:NEW

 2.运行状态(RUNNING)

分为RUNNING(运行状态)Ready(就绪状态).

  public static void main(String[] args) {
        Thread t=new Thread(()->{
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        });
        t.start();
        System.out.println("t的线程状态:"+t.getState()); // 输出线程的当前状态
    }
t的线程状态:RUNNABLE

 3.等待状态(WAITING)

等待状态下,没有时间限制,等被notify或者notifyAll唤醒

 public static void main(String[] args) throws InterruptedException {
        // 获取当前线程对象,用于后续检查线程状态
        Thread mainT=Thread.currentThread();
        
        // 使用lambda表达式创建一个新的线程,该线程用于展示线程的运行和状态检查
        Thread t=new Thread(()->{
            try {
                // 使新线程睡眠1秒,模拟耗时操作
                Thread.sleep(1000);
            }catch (InterruptedException e){
                // 如果线程在睡眠时被中断,打印异常信息
                e.printStackTrace();
            }
            // 输出线程的当前状态
            System.out.println("main线程状态:"+mainT.getState());
        });
        
        // 启动新线程,使其开始执行
        t.start();
        // 主线程等待新线程完成
        t.join();
    }
main线程状态:WAITING

4.超时等待(TIMED_WAITING)

TIMED_WAITING线程在等待唤醒,但设置了时限,当到达时限,会自动唤醒线程。

 public static void main(String[] args) throws InterruptedException {
        // 创建一个线程,该线程执行一个lambda表达式,使线程睡眠1秒
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(1000); // 线程睡眠1000毫秒
            } catch (InterruptedException e) {
                e.printStackTrace(); // 捕获InterruptedException异常并打印堆栈跟踪
            }
        });
        t.start();
        //让main线程休眠
        Thread.sleep(50);
        System.out.println(t.getState());
    }
TIMED_WAITING

5.阻塞状态(BLOCKED)

 当进入到synchronized关键字修饰的方法或者代码块时,线程处于阻塞状态。

示例:两个线程争同一个锁对象,此时会引起锁竞争,没有获取到锁的线程,会进入阻塞状态,等待拿到锁的线程将锁释放。

   public static void main(String[] args) {
        final Object o=new Object();
        Thread t=new Thread(()->{
            synchronized (o) {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        Thread tt=new Thread(()->{
            synchronized (o) {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        t.start();
        tt.start();
        System.out.println("t的线程状态:"+t.getState()); // 输出线程的当前状态
        System.out.println("tt的线程状态:"+tt.getState()); // 输出线程的当前状态
    }
t的线程状态:RUNNABLE
tt的线程状态:BLOCKED

 6.终止状态(TERMINATED)

当线程执行结束,就处于终止状态。

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

/**
     * 程序入口
     * @param args 命令行参数
     * @throws InterruptedException 如果线程被中断
     */
    public static void main(String[] args) throws InterruptedException {
        // 创建一个线程,该线程将休眠1秒
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        // 启动线程
        t.start();
        // 等待线程结束
        t.join();
        // 输出线程的状态
        System.out.println(t.getState());
    }
TERMINATED

 三.线程状态间的转换

1.创建一个线程处于new,让其转换为running状态。可以看到线程t的状态为

NEW->RUNNING->TERMINATED

 public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
        });
        System.out.println("t线程的状态"+t.getState());
        t.start();
        System.out.println("t线程的状态"+t.getState());
        t.join();
        System.out.println("t线程的状态"+t.getState());
    }
t线程的状态NEW
t线程的状态RUNNABLE
t线程的状态TERMINATED

2.将处于running的线程转换为wait 。(调用wait或者join都可)

 这里以join为例。

 public static void main(String[] args) throws InterruptedException {
        Thread main = Thread.currentThread();
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("main线程状态:"+main.getState());

        });
        t.start();
        System.out.println("main线程状态:"+main.getState());
        t.join();
    }
main线程状态:RUNNABLE
main线程状态:WAITING

wait方法:一般在synchronized修饰的代码块或者方法中使用。

调用wait会发生两件事:

(1)线程解锁“锁对象” (2)被解锁的线程会进入waiting状态。 

在使用wait方法时,需要先对线程加锁,否则会报错。因为wait会先解锁再进入等待状态

示例:

 public static void main(String[] args) throws InterruptedException {
        final Object locker = new Object();
        Thread t1=new Thread(()->{
            synchronized (locker){
                    try {
                        locker.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
            }
        },"t1");
        t1.start();
        System.out.println("t线程的状态"+t1.getState());
        Thread.sleep(50);
        System.out.println("t线程的状态"+t1.getState());
    }
t线程的状态RUNNABLE
t线程的状态WAITING

 但如果我们使用wait带参数的方法,那么状态还会是WAITING吗?

  public static void main(String[] args) throws InterruptedException {
        final Object locker = new Object();
        Thread t1=new Thread(()->{
            synchronized (locker){
                    try {
                        locker.wait(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
            }
        },"t1");
        t1.start();
        System.out.println("t线程的状态"+t1.getState());
        Thread.sleep(50);
        System.out.println("t线程的状态"+t1.getState());
    }
t线程的状态RUNNABLE
t线程的状态TIMED_WAITING

可以看到,当我们使用带参数的wait方法时,此时t的状态转变为TIMED_WAITING

3.若多个线程尝试获取同一个锁,那么就会引起对锁的竞争,即"锁竞争”。没用获取到线程的锁将会进入阻塞状态(BLOCKED),等线程获取到锁,才能执行。

示例:

    /**
     * 主函数,演示线程间的状态变化
     * @param args 命令行参数
     * @throws InterruptedException 当线程被中断时抛出此异常
     */
    public static void main(String[] args) throws InterruptedException {
        // 创建一个对象作为锁
        final Object locker = new Object();
        
        // 创建线程t1,使用锁对象进行同步操作
        Thread t1=new Thread(()->{
            // 使用锁进行同步控制,形成死循环
            synchronized (locker){
                while(true){
                    ;
                }
            }
        },"t1");
        
        // 启动线程t1
        t1.start();
        // 输出线程t1的当前状态
        System.out.println("t1线程的状态"+t1.getState());
        
        // 创建线程t2,也使用锁对象进行同步操作
        Thread t2=new Thread(()->{
            // 使用锁进行同步控制,但没有进行循环,所以很快释放锁
            synchronized (locker){
               ;
            }
        },"t2");
        
        // 启动线程t2
        t2.start();
        // 输出线程t2的当前状态
        System.out.println("t2线程的状态"+t2.getState());
        
        // 主线程等待50毫秒,以观察线程t2的状态变化
        Thread.sleep(50);
        // 再次输出线程t2的当前状态,以观察其变化
        System.out.println("t2线程的状态"+t2.getState());
    }
t1线程的状态RUNNABLE
t2线程的状态RUNNABLE
t2线程的状态BLOCKED

我们可以打开jconsole进行查看。

四.总结 

 

当我们创建一个线程对象时,此时线程就处于NEW状态。

调用start方法,会让线程进行运行状态,其中又分为RUNNING(运行中状态)和Ready(就绪状态)。

若想要让RUNNABLE中的线程进入WAITING(等待状态),可以使用wait或者join方法。想唤醒WAITING中的线程,需要使用notify或者nofityAll方法来唤醒。

可以使用带参数的join、sleep和wait方法,让RUNNABLE中的线程进入限时等待(TIMED_WAITING),当达到时时限,会自动唤醒线程。

 RUNNABLE中的线程若想出发BLOCKED状态,需要触发锁竞争

当线程中线程体的内容都执行完之后,就会进入TERMINATED(终止状态)


线程状态的讲解就先到这里了~

若有不足,欢迎指正~

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

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

相关文章

mindspore框架下Pix2Pix模型实现真实图到线稿图的转换|(三)Pix2Pix模型训练与模型推理

mindspore框架下Pix2Pix模型实现真实图到线稿图的转换 mindspore框架下Pix2Pix模型实现真实图到线稿图的转换|(一)dataset_pix2pix数据集准备mindspore框架下Pix2Pix模型实现真实图到线稿图的转换|(二)Pix2Pix模型构建mindspore框…

Google Gemma2 2B:语言模型的“小时代”到来?

北京时间8月1日凌晨(当地时间7月31日下午),Google发布了其Gemma系列开源语言模型的更新,在AI领域引发了巨大的震动。Google Developer的官方博客宣布,与6月发布的27B和9B参数版本相比,新的2B参数模型在保持…

python实现consul的服务注册与注销

我在使用consul的时候主要用于prometheus的consul服务发现,把数据库、虚拟机信息发布到consul,prometheus通过consul拿到数据库、虚拟机信息去采集指标信息。 此篇文章前提是已经安装好consul服务以后,安装consul请参考二进制方式部署consul…

Nat网络地址转换实验

一、实验拓扑 二、实验要求 三、实验思路 四、实验展示 1.接口IP配置 telnet路由器 r1 r2 r3 pc2 2.全网可达(给边界路由器,私家路由器写上缺省 ,还要用到nat地址转换,多对多一对多,端口映射)因为左右…

第22集《大佛顶首楞严经》

请大家打开讲义第四十九页,“寅三、大众茫然”。 我们要是读《金刚经》,就知道整个修学的方向。《金刚经》就是讲到,一个菩萨发了菩提心,心中有目标,要能够上求佛道,下化众生,但是他不知道“云…

探索味蕾新境界:嘴尚绝卤味,一口难忘的美味传奇

在美食的浩瀚星空中,总有一些味道能够穿越时光的长河,直击人心最柔软的部分,让人回味无穷。今天,就让我们一起走进“嘴尚绝”卤味的世界,感受那份独特而令人难以忘怀的口感之美。 一、卤味之魂,匠心独运 “…

CTF web bibibi题型

CTF web bibibi题型 1.进入网站 在kali中使用Dirsearch对地址进行目录扫描,发现robots.txt 网址内加入 /robots.txt 进入网址 /fl4gi5Here.php 找到flag

未来五年,网络安全有没有发展前途,零基础转行难不难?

在被新冠疫情常态化影响的今天,职场当中呈现出了严重的两极分化现象,具体的表现形式为: 一些人薪资翻倍、愈加繁忙,另一些人则加入了失业大军、不知所措;一些行业实现了井喷式增长,一些行业却不断裁员、随…

Apache解析漏洞

一、apache_parsing 在Apache1.x/2.x中Apache 解析文件的规则是从右到左开始判断解析,如果后缀名为不可识别文件解析,就再往左判断。如1.php.xxxxx 1、进入Vulhub靶场并执行以下命令启动靶场 2、只要一个文件含有.php后缀的文件即将被识别成PHP文件&am…

即时通讯和即时通信,即时通讯和实时通信

在当今数字化时代,即时通讯和实时通信已成为人们日常生活和工作中不可或缺的一部分。尽管这两个概念经常被混淆使用,但它们在本质和应用上存在一些区别和联系。同时,企业级即时通讯平台WorkPlus对于提升企业内部沟通和协作也有着重要的作用。…

Java面试八股之简述spring boot的目录结构

简述spring boot的目录结构 Spring Boot 项目遵循标准的 Maven 或 Gradle 项目布局,并且有一些约定的目录用于组织不同的项目组件。下面是一个典型的 Spring Boot 项目目录结构: src/main/java:包含所有的 Java 源代码,通常按包组…

8个高质量PPT模板网站,免费下载

演示文稿已经成为交流和展示想法的重要工具。而一个引人注目、内容精彩的PPT演示,不仅可以让观众留下深刻的印象,还能有效地传达信息和观点。分享八个备受推崇的高质量PPT模板网站,这些网站提供各种各样的模板,涵盖了不同主题、风…

史上最快在IDEA中创建类,只需要 ctrl + 鼠标左键双击 就可以调出创建类的窗口(全网首创)

文章目录 1、正常创建类的步骤2、改进方案 1、正常创建类的步骤 需要首先鼠标右键一次,点击新建,再点击 Java类,过于麻烦 2、改进方案 我们只需要自定义设置创建类的快捷键即可 找到设置、按键映射、主菜单、文件、文件打开操作、打开项目…

有哪些因素会影响谷歌ASO优化效果呢

目前在Google Play上,已超过5.3亿的移动应用。未来还会有更多的移动应用涌入。开发者都希望自己的应用,最具有竞争力,并且可以获得大量免费流量。ASO是Google Play最重要的策略之一,而影响谷歌ASO优化效果的因素有很多&#xff0c…

欧拉系统如果数据库忘记密码的解决办法

如果数据库忘记密码,该怎么办 systemctl stop mariadb #先关闭数据库 mysqld_safe --skip-grant-tables& #跳过权限表的检查 mysql #现在可以不通过密码就能进入mysql了 flush privileges; #刷新权限 alter user rootlocalhost ide…

【IEEE出版 | 连续五届稳定EI检索】第六届机器学习、大数据与商务智能国际会议(MLBDBI 2024)

IEEE出版 | MLBDBI 2023会后4个半月内完成EI检索 第六届机器学习、大数据与商务智能国际会议(MLBDBI 2024) 2024 6th International Conference on Machine Learning, Big Data and Business Intelligence 重要信息 大会官网: 会议时间&a…

二叉树链式结构的实现(递归的暴力美学!!)

前言 Hello,小伙伴们。你们的作者菌又回来了,前些时间我们刚学习完二叉树的顺序结构,今天我们就趁热打铁,继续我们二叉树链式结构的学习。我们上期有提到,二叉树的的底层结构可以选为数组和链表,顺序结构我们选用的数…

大数据Flink(一百零六):什么是阿里云实时计算Flink版

文章目录 什么是阿里云实时计算Flink版 一、产品概述 二、产品架构 三、产品优势 什么是阿里云实时计算Flink版 阿里云实时计算Flink版是一套基于Apache Flink构建的⼀站式实时大数据分析平台,提供端到端亚秒级实时数据分析能力,并通过标准SQL降低业…

openEuler 自定义ISO制作(logo,名称,ISO)

前言 oecustom (openEuler customize) 是一套关于 openEuler iso 格式光盘映像的定制工具集。 工具用途iso_custom用于定制 openEuler iso 镜像,可以定制 openEuler iso 镜像的系统名称和安装界面图标等iso_cut用于裁剪 openEuler iso 镜像,参考 oemak…

暴食之史莱姆(河南萌新2024)

思路&#xff1a;单调栈&#xff08;分别统计左边小于等于当前大小的数量&#xff09; #include <bits/stdc.h>using namespace std; typedef long long ll; typedef double db; typedef long double ldb; typedef pair<int, int> pii; typedef pair<ll, ll>…