首页 > 其他分享 >一个线程,从“生”到“死”经历的过程

一个线程,从“生”到“死”经历的过程

时间:2024-02-05 09:35:10浏览次数:27  
标签:状态 RUNNABLE Java Thread 经历 WAITING 线程 过程

本文分享自华为云社区《面试必问 | 一个线程从创建到消亡要经历哪些阶段?》,作者: 冰 河。

今天,我们就结合 操作系统线程和编程语言线程 再次深入探讨线程的生命周期问题,线程的生命周期其实没有我们想象的那么简单!!

理解线程的生命周期本质上理解了生命周期中各个节点的状态转换机制就可以了。接下来,我们分别就 通用线程生命周期和Java语言的线程生命周期 分别进行详细说明。

通用的线程生命周期

通用的线程生命周期总体上可以分为五个状态:初始状态、可运行状态、运行状态、休眠状态和终止状态。

我们可以简单的使用下图来表示这五种状态。

640.jpg

初始状态

线程已经被创建,但是不允许分配CPU执行。需要注意的是:这个状态属于编程语言特有,这里指的线程已经被创建,仅仅指在编程语言中被创建,在操作系统中,并没有创建真正的线程。

可运行状态

线程可以分配CPU执行。此时,操作系统中的线程被成功创建,可以分配CPU执行。

运行状态

当操作系统中存在空闲的CPU,操作系统会将这个空闲的CPU分配给一个处于可运行状态的线程,被分配到CPU的线程的状态就转换成了运行状态

休眠状态

运行状态的线程调用一个阻塞的API(例如,以阻塞的方式读文件)或者等待某个事件(例如,等待某个条件变量等),线程的状态就会转换到休眠状态。**此时线程会释放CPU资源,休眠状态的线程没有机会获得CPU的使用权。**一旦等待的条件出现,线程就会从休眠状态转换到可运行状态。

终止状态

线程执行完毕或者出现异常就会进入终止状态,终止状态的线程不会切换到其他任何状态,这也意味着线程的生命周期结束了。

以上就是通用的线程生命周期,下面,我们再看对比看下Java语言中的线程生命周期。

Java中的线程生命周期

Java中的线程生命周期总共可以分为六种状态:初始化状态(NEW)、可运行/运行状态(RUNNABLE)、阻塞状态(BLOCKED)、无时限等待状态(WAITING)、有时限等待状态(TIMED_WAITING)、终止状态(TERMINATED)。

需要大家重点理解的是:虽然Java语言中线程的状态比较多,但是,其实在操作系统层面,Java线程中的阻塞状态(BLOCKED)、无时限等待状态(WAITING)、有时限等待状态(TIMED_WAITING)都是一种状态,即通用线程生命周期中的休眠状态。也就是说,只要Java中的线程处于这三种状态时,那么,这个线程就没有CPU的使用权。

理解了这些之后,我们就可以使用下面的图来简单的表示Java中线程的生命周期。

640.jpg

我们也可以这样理解阻塞状态(BLOCKED)、无时限等待状态(WAITING)、有时限等待状态(TIMED_WAITING),它们是导致线程休眠的三种原因!

接下来,我们就看看Java线程中的状态是如何转化的。

RUNNABLE与BLOCKED的状态转换

只有一种场景会触发这种转换,就是线程等待synchronized隐式锁。synchronized修饰的方法、代码块同一时刻只允许一个线程执行,其他的线程则需要等待。

此时,等待的线程就会从RUNNABLE状态转换到BLOCKED状态。当等待的线程获得synchronized隐式锁时,就又会从BLOCKED状态转换到RUNNABLE状态。

这里,需要大家注意:线程调用阻塞API时,在操作系统层面,线程会转换到休眠状态。但是在JVM中,Java线程的状态不会发生变化,也就是说,Java线程的状态仍然是RUNNABLE状态。

JVM并不关心操作系统调度相关的状态,在JVM角度来看,等待CPU使用权(操作系统中的线程处于可执行状态)和等待IO操作(操作系统中的线程处于休眠状态)没有区别,都是在等待某个资源,所以,将其都归入了RUNNABLE状态。

我们平时所说的Java在调用阻塞API时,线程会阻塞,指的是操作系统线程的状态,并不是Java线程的状态。

RUNNABLE与WAITING状态转换

线程从RUNNABLE状态转换成WAITING状态总体上有三种场景。

场景一

获得synchronized隐式锁的线程,调用无参的Object.wait()方法。此时的线程会从RUNNABLE状态转换成WAITING状态。

场景二

调用无参数的Thread.join()方法。其中join()方法是一种线程的同步方法。例如,在threadA线程中调用threadB线程的join()方法,则threadA线程会等待threadB线程执行完。

而threadA线程在等待threadB线程执行的过程中,其状态会从RUNNABLE转换到WAITING。当threadB执行完毕,threadA线程的状态则会从WAITING状态转换成RUNNABLE状态。

场景三

调用LockSupport.park()方法,当前线程会阻塞,线程的状态会从RUNNABLE转换成WAITING。

调用LockSupport.unpark(Thread thread)可唤醒目标线程,目标线程的状态又会从WAITING状态转换到RUNNABLE。

RUNNABLE与TIMED_WAITING状态转换

总体上可以分为五种场景。

场景一

调用带超时参数的Thread.sleep(long millis)方法;

场景二

获得synchronized隐式锁的线程,调用带超时参数的Object.wait(long timeout)参数;

场景三

调用带超时参数的Thread.join(long millis)方法;

场景四

调用带超时参数的LockSupport.parkNanos(Object blocker, long deadline)方法;

场景五

调用带超时参数的LockSuppor.parkUntil(long deadline)方法。

从NEW到RUNNABLE状态

Java刚创建出来的Thread对象就是NEW状态,创建Thread对象主要有两种方法,一种是继承Thread对象,重写run()方法;另一种是实现Runnable接口,重写run()方法。

注意:这里说的是创建Thread对象的方法,而不是创建线程的方法,创建线程的方法包含创建Thread对象的方法。

继承Thread对象

public class ChildThread extends Thread{
    @Override
    public void run(){
        //线程中需要执行的逻辑
    }
}
//创建线程对象
ChildThread childThread = new ChildThread();

实现Runnable接口

public class ChildRunnable implements Runnable{
    @Override
    public void run(){
        //线程中需要执行的逻辑
    }
}
//创建线程对象
Thread childThread = new Thread(new ChildRunnable());

处于NEW状态的线程不会被操作系统调度,因此也就不会执行。Java中的线程要执行,就需要转换到RUNNABLE状态。从NEW状态转换到RUNNABLE状态,只需要调用线程对象的start()方法即可。

//创建线程对象
Thread childThread = new Thread(new ChildRunnable());
//调用start()方法使线程从NEW状态转换到RUNNABLE状态
childThread.start();

RUNNABLE到TERMINATED状态

线程执行完run()方法后,或者执行run()方法的时候抛出异常,都会终止,此时为TERMINATED状态。如果我们需要中断run()方法,可以调用interrupt()方法。

 

点击关注,第一时间了解华为云新鲜技术~

 

标签:状态,RUNNABLE,Java,Thread,经历,WAITING,线程,过程
From: https://www.cnblogs.com/huaweiyun/p/18007394

相关文章

  • Drvsetup.dll 是 Windows 操作系统中的一个动态链接库文件,用于设备驱动程序的安装和配
     Drvsetup.dll是Windows操作系统中的一个动态链接库文件,用于设备驱动程序的安装和配置过程中。该文件通常位于C:\Windows\System32文件夹下。Drvsetup.dll主要负责设备驱动程序的安装和配置过程中的一些核心功能,包括驱动程序的复制、注册、配置和卸载等。在设备驱动程序......
  • java----多线程
    1.什么是线程和进程?进程好比一个软件,线程好比软件中的一个功能。一个进程包含了多个线程,举例:比如360软件中木马查杀;买票的时候,火车站就是一个进程,各个窗口表示线程。并行与并发之间的区别:好比做饭吧,几个厨师分别同时做不同的食物-------------------并行---------------......
  • 【Java基础】Java线程的六种状态详解
    NEW状态当创建一个Thread对象但尚未调用其start()方法时,线程处于NEW状态。在这个状态下,线程并未启动,仅完成了初始化阶段。RUNNABLE状态RUNNABLE是Java中较为特殊的一个状态,它涵盖了传统操作系统中的就绪和运行两种状态。当线程已启动且CPU调度器为其分配了时间片或线程正在等待系......
  • yarn安装太慢,如何多线程安装依赖
    Yarn本身设计时就考虑到了并行安装依赖以提高速度,它默认使用多线程来下载和安装包。当执行yarninstall时,Yarn会利用所有可用的CPU核心,并通过其内部的并行化机制来加速安装过程。如果你发现Yarn在安装依赖时仍然显得较慢,可以尝试以下方法来优化:启用网络代理:如果你的网......
  • 软件安装的过程中都做了些什么
    转载自软件安装的过程中都做了些什么软件在安装时,到底做了些什么?大家每天都在用电脑,可能也经常在自己的电脑上安装软件。就算自己没安装过,至少也看到人家安装过软件。在这里,我不是想教你怎么安装软件,而是想向你展示,软件在安装的过程中,到底都做了些什么动作?为什么有些软件要安装,......
  • threadlocal 线程本地变量,线程独享
         ......
  • vue2中使用富文本编辑器tinyMCE全过程
    TinyMCE中文文档地址:http://tinymce.ax-z.cn/1.安装[email protected]$npminstall@tinymce/[email protected].将node_modules/tinymce文件夹下的plugins文件夹和skins文件夹直接复制到public/tinymce目录下3.引入汉语包在plublic/tinymce文......
  • C++多线程 第三章 在线程间共享数据
    第三章在线程间共享数据共享数据基本问题如果所有共享数据都只读,那就没有问题.不变量(invariants):对特定数据结构总为真的语句.例如:"该变量表示线程数量."修改线程之间共享数据的一个常见潜在问题就是破坏不变量.竞争条件(racecondition):线程竞争执行各自的操作,导......
  • 【MybatisPuls】如何调用DM存储过程并返回多结果集
    一、创建DM存储过程CREATEORREPLACEPROCEDUREyour_procedure(result1OUTCURSOR,result2OUTCURSOR)ASBEGIN--打开第一个结果集OPENresult1FORSELECTtop10*FROM表;--打开第二个结果集OPENresult2FORSELECTtop10*FROM表;END;--SQL测试存储过......
  • 深入浅出Java多线程(七):重排序与Happens-Before
    引言大家好,我是你们的老伙计秀才!今天带来的是[深入浅出Java多线程]系列的第七篇内容:重排序与Happens-Before。大家觉得有用请点赞,喜欢请关注!秀才在此谢过大家了!!!在上一篇文章中,我们简单提了一下重排序与Happens-Before。在这篇文章中我们将深入讲解一下重排序与Happens-Before,然......