线程的生命周期以及其中的方法

article/2025/9/12 9:36:59

文章目录

  • 线程的生命周期
    • 线程的5种状态
  • 线程的基本方法
    • 线程等待:wait 方法
    • 线程睡眠:sleep 方法
    • 线程让步:yield 方法
    • 线程中断:interrupt 方法
    • 线程加入:join 方法
    • 线程唤醒:notify 方法
    • 后台守护线程:setDaemon 方法
  • sleep 和wait 方法的区别
  • start 和 run 方法的区别

线程的生命周期

线程的生命周期分为新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)这 5 种状态。在系统运行过程当中不断会有新的线程被创建,旧的线程在执行完毕后被清理,线程在排队获取共享资源或者锁时将被阻塞,因此运行中线程就是在就绪、阻塞、运行状态之间来回切换。线程的具体状态转换流程如下图所示:
在这里插入图片描述

其执行流程:

  1. 调用 new 方法创建一个线程,这时线程处于新建状态。
  2. 调用 start 方法启动一个线程,这时线程处于就绪状态。
  3. 处于就绪状态的线程等待线程获取到 CPU 资源,在等待其获取 CPU 资源后线程会执行 run() 方法进行运行状态。
  4. 正在执行的线程在调用 yield() 方法或失去处理资源时,会再次进入就绪状态。
  5. 正在执行的线程在执行 sleep() 方法、I/O 阻塞、等待同步锁、等待通知、调用 suspeed() 方法等操作后,会挂起并进入阻塞状态,进入 Blocked 池。
  6. 阻塞状态的线程由于出现 sleep 时间已到、I/O 方法返回、获得同步锁、收到通知、调用 resume 方法等情况,会再次进入就绪状态,等待 CPU 时间片的轮询。该线程在获取 CPU 资源后,会再次进入到运行状态。
  7. 处于运行状态的线程,在调用 run 方法或 call 方法正常执行完成、调用 stop 方法停止下线程或者程序执行错误导致异常退出时,会进入死亡状态。

线程的5种状态

新建状态 New
在 Java 当中使用 new 关键字创建了一个线程,新创建的线程将处于新建状态。在创建线程的时候主要是为线程分配内存并初始化其成员变量的值。

就绪状态 Runnable
新建的线程对象在调用 start 方法之后将转为就绪状态。此时 JVM 完成了方法调用栈和程序计数器的创建,等待该线程的调度和运行。

运行状态 Running
就绪状态的线程在竞争到 CPU 的使用权并开始执行 run() 方法的线程执行体时,会转为运行状态,处于运行状态的线程的主要任务就是执行 run() 方法中的逻辑代码。

阻塞状态 Blocked
运行中的线程会主动或被动地抛弃 CPU 的使用权并暂停运行,此时该线程将转为阻塞状态,直到再次进入可运行状态,才有机会再次竞争到 CPU 使用权并转为运行状态。阻塞的状态可以分为以下三种:

  1. 等待阻塞:在运行态的线程调用 o.wait() 方法时,JVM 会把该线程放入等待队列 (Waitting Queue)中,线程转为阻塞状态。
  2. 同步阻塞:在运作状态的线程尝试获取正在被其他线程占用的对象同步锁时, JVM 会把线程放入到锁池当(Lock Pool)中,此时线程装换为阻塞状态。
  3. 其他阻塞:运行状态的线程在执行 Thread.sleep(long ms)、Thread.join()或者发出 I/O 请求时, JVM 会把该线程转为阻塞状态。直到 sleep() 状态超时、Thread.join()等待线程终止或超时,或者 I/O 处理完毕,线程才重新转为可运行状态。

线程死亡 Dead
线程在以下三种方式结束后转换为死亡状态

线程正常结束:run 方法或 call 方法执行完成
线程异常退出:运行中的线程抛出一个 Error 或未捕获的 Exception,线程异常退出。
手动结束:调用线程对象的 stop 方法手动结束运行中的线程(该方式会瞬间释放线程占用的同步对象锁,导致锁混乱和死锁,不推荐使用)

线程的基本方法

线程相关的基本方法有 wait、notify、notifyAll、sleep、join、yield 等,这些方法控制线程的运行,并影响线程状态之间的转换。

线程等待:wait 方法

调用 wait() 方法的线程会进入 WAITING 状态,只有等到其他线程的通知或被中断后才会返回。需要注意的是,在调用 wait() 方法后会释放对象的锁,因此 wait() 方法一般是被使用与同步方法或同步代码块当中。

线程睡眠:sleep 方法

调用 sleep 方法会导致当前线程休眠。与 wait() 方法不同的是,sleep() 方法不会释放当前占有的锁,会导致线程进入 TIMED-WAITING 状态,而 wait 方法会导致当前线程进入 WAITING 状态。

线程让步:yield 方法

调用 yield 方法会使当前线程让出(释放)CPU 执行的时间片,与其他线程一起重新竞争 CPU 时间片。在一般情况下,优先级高的线程更有可能竞争到 CPU 时间片,但这不是绝对的,有的操作系统对线程的优先级并不敏感。

线程中断:interrupt 方法

interrupt 方法用于向线程发行一个终止通知信号,会影响该线程内部的一个中断标识位,线程本身并不会因为调用了 interrupt 方法而改变状态(阻塞、终止等)。状态的具体变化需要等待接收到中断标识的程序的最终结果来判定。对 interrupt 方法的理解需要注意以下 4 个核心的问题:

  1. 调用 interrupt 方法并不会中断一个正在运行的线程,也就是说处于 Running 状态的线程并不会因为调用了 interrupt 方法而终止,仅仅改变了内部维护的中断标识位而已。
    JDK 源码:
    public static boolean interrupted() {return currentThread().isInterrupted(true);}public boolean isInterrupted() {return isInterrupted(false);}
  1. 若因为调用了 sleep 方法而使线程处于 TIMED-WATING 状态,则这时调用 interrupt 方法会抛出 InterruptedException,使线程提前结束 TIME-WATING 状态。
  2. 许多声明抛出 InterruptedException 的方法如 Thread.sleep(long mills),在抛出异常前都会清除中断标识位,所以在抛出异常后调用 isInterrupted 方法将会返回 false。
  3. 中断状态是线程所固有的一个标识位,可以通过此标识位安全终止线程。比如,在想终止一个线程的时候,可以先调用该线程的 interrupt 方法,然后在线程的 run 方法中根据该线程 isInterrupted 方法的返回状态值安全终止线程。
public class ThreadMethod {public static void main(String[] args) {ThreadDemo threadDemo = new ThreadDemo();threadDemo.start();threadDemo.interrupt();}
}class ThreadDemo extends Thread {@Overridepublic void run() {// 开始时线程中断标识状态为:System.out.println("初始默认的标识为:" + Thread.currentThread().isInterrupted());if (Thread.currentThread().isInterrupted()) {try {System.out.println("线程sleep睡眠1秒");sleep(1000);//在这sleep了。就将线程中断改为 false,抛出异常,并立刻结束睡眠} catch (InterruptedException e) {System.out.println("出现异常");// 线程中断异常标识为:System.out.println("出现异常后标识符为:" + Thread.currentThread().isInterrupted());Thread.currentThread().interrupt(); //重新设置中断标识}}System.out.println("重置后标识符为:" + Thread.currentThread().isInterrupted());if (Thread.currentThread().isInterrupted()) {try {sleep(1000);//抛出异常} catch (InterruptedException e) {System.out.println("因为sleep抛出异常");e.printStackTrace();}}System.out.println("---------当前线程任务执行完啦---------");}
}

在这里插入图片描述

线程加入:join 方法

join 方法是用于等待其他线程终止,如果在当前线程中调用一个线程的 join 方法,则当前线程会转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转为就绪状态,等待获取 CPU 的使用权。
在很多情况下,主线程生成并启动子线程,需要等待子线程返回结果并收集和处理再退出,这时就要用到 join 方法。

System.out.println("这个是主线程");
// 生成子线程
ChildThread childThread = new ChildThread();
// 执行子线程
childThread.join();//主线程(当前线程)阻塞等待子线程 childThread 执行线束
System.out.println("子线程join启动后,执行完毕后开始运行主线程");

线程唤醒:notify 方法

Object 类有个 notify 方法,用于唤醒在此时对象监视器上等待的一个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,唤醒时选择是任意的。

我们通常调用其中一个对象的 wait 方法在对象的监视器上等待,直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方法与在该对象上主动同步的其他线程竞争。类似的方法还有 notifyAll ,用于唤醒在监视器上等待的所有线程。

后台守护线程:setDaemon 方法

setDaemon 方法用于定义一个守护线程,也叫做“服务线程”,该线程是后台线程。后台守护线程有一个特性,即为用户线程提供公共服务,在没有用户线程可服务时会自动离开。

守护类线程的优先级较低,用于为系统中的其他对象和线程提供服务。将一个用户线程设置为守护现场的方法是在线程对象创建之前用线程对象的 setDacemon(true) 来设置的。

在后台守护线程中定义线程也是后台守护线程。后台守护线程是 JVM 级别的,比如垃圾回收线程就是一典型的守护线程,在我们的程序中不再有任何线程运行时,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以在回收 JVM 上仅剩的线程时,垃圾回收器线程会自动离开。它始终在低级别的状态下运行,用于实时监控和管理系统中的可回收资源。

守护线程是运行在后台的一种特殊线程,其独立于控制终端并且周期性地执行某种任务或等待处理某些已发生的事件,也就是说,守护线程是不依赖于终端的,但是依赖于 JVM ,与 JVM “同生共死”。在 JVM 中的所有线程都是守护线程时,JVM 就可以退出了,如果还有一个或一个以上的非守护现场,则 JVM 是不会退出的。


这些方法对线程的影响:
在这里插入图片描述

sleep 和wait 方法的区别

  1. sleep 方法是属于 Thread 类,wait 方法是属于 Object 类。
  2. sleep 方法暂停执行指定时间,让出 CPU 给其他线程,但其监控状态依然保持,在指定的时间过后自动回复为运作状态。
  3. 在调用 sleep 方法的过程中,线程不会释放对象锁。
  4. 在调用 wait 方法时,线程会放弃对象锁,进入等待此对象的等待锁池,只有针对此对象调用 notify 方法后,该线程才能进入对象锁池准备获取对象锁,并进入运行状态。

start 和 run 方法的区别

  1. start 方法用于启动线程,真正实现了多线程运行。在调用了线程的 start 方法后,线程会在后台执行,无须等待 run 方法体的代码执行玩不,就可以继续执行下面的代码。
  2. 在通过调用 Thread 类的 start 方法启动一个线程时,此线程时处于就绪状态,并没有运行。
  3. run 方法也叫做线程体,包含了要执行的线程的逻辑代码,在调用 run 方法后,线程会进入运行状态,开始运行 run 方法中的代码。在run 方法运行结束后,该线程终止, CPU 再次调度其他线程。

下一章:===》线程终止的 4 种方式


http://chatgpt.dhexx.cn/article/HTua6aAl.shtml

相关文章

Java多线程 (五)—— 线程的生命周期

文章目录 前言1、线程生命周期1.1、NEW(新建)1.2、RUNNABLE(可运行)1.3、BLOCKED(阻塞)1.4、WAITING(等待)1.5、TIMED_WAITING(计时等待)1.5、TERMINATED&…

线程的生命周期及五种基本状态

一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点。掌握了上图中的各知识点,Java中的多线程也就基本上掌握了。主要包括: Java线程具有五…

浅谈线程的生命周期

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态…

java中,线程的生命周期

java中,线程的生命周期 一、什么是线程? 见之前写的。 当线程启动后(线程对象调用start方法),它不能一直"独占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次…

JAVA 多线程并发

1.1.1. JAVA 并发知识库 1.1.2. JAVA 线程实现/创建方式 1.1.2.1. 继承 Thread 类 Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它…

攻防世界 web高手进阶区 10分题 TimeKeeper

前言 继续ctf的旅程 开始攻防世界web高手进阶区的10分题 本文是TimeKeeper的writeup 解题过程 进入界面 惯例源码御剑 源码没发现 御剑扫到一个Flask debug console 先正常注册登录 尝试在注册和登录界面sqli 失败 没有别的头绪 就尝试研究debug 参考Flask debug pin安全问…

【Linux时钟系统】

Linux系统时间 时间设备![在这里插入图片描述](https://img-blog.csdnimg.cn/7c50db7d72ba4ff5a0f1264629769184.png)RTC时钟系统计时设备(吴斌个人主页)定时通知设备(吴斌个人主页)系统启动过程中的时钟系统SMP初始化阶段Linux时钟中断低精度定时器的使…

linux时间子系统 - clocksource/timekeeper

linux时间子系统中有一个很重要的记录时间的模块就是timekeeper,而timekeeper需要底层clock硬件设备的支持,每一个设备用结构体clocksource来表示,注册进系统的每一个clocksource会凭借优先级最终被选择成为timekeeper的时钟源。 1. clocksou…

【Java】Timer和TimerTask详解

以下内容根据 The JavaTM Tutorial 和相关API doc翻译整理,以供日后参考: 1.概览 Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。它可以计划执行一个任务一次或反复多次。 TimerTask一个抽象类,它的子类代表一个可以…

【Linux内核代码分析1】Linux时间子系统及HRTIMER实现

Linux时间子系统软件架构 (1)嵌入式设备需要较好的电源管理策略。传统的linux会有一个周期性的时钟,即便是系统无事可做的时候也要醒来,这样导致系统不断的从低功耗(idle)状态进入高功耗的状态。这样的设计…

Linux Time

1、Linux时钟框架 上图是linux时钟框架一个经典的描述。本质上linux各种时钟架构和服务是基于硬件提供的两种timer而构建的。 1、定时Timer 这类timer每个cpu都有一个独立的,称为local timer。这类timer的中断一般都是PPI(Private Peripheral Interrup…

Linux时间子系统基础(三):时间的维护者:timekeeper

本系列文章的前两节讨论了用于计时的时钟源:clocksource,以及内核内部时间的一些表示方法,但是对于真实的用户来说,我们感知的是真实世界的真实时间,也就是所谓的墙上时间,clocksource只能提供一个按给定频…

攻防世界--TimeKeeper

将price和id加上引号,触发debug 联想到扫描目录出现的/console Flask debug pin安全问题 这种方法有点难,换种方法 目录穿越 可以成功读取任意文件,获得flag。

html 时间控件滚动选择器,TimePicker

TimePicker滚动的列表选择器,可用于时间选择。多个TimePicker组合起来可实现复杂的应用,如效果图中,俩个TimePicker组合,一个表示"分"、一个表示"秒"。 效果图: 如何使用xml创建 android:id"…

Linux时间子系统之时间维护层(Time Keeper)

时间维护层会收到Tick层的周期调用,每次调用的周期是由内核参数决定的。在此期间,时间维护层可以读取时钟源设备的周期数,从而感知时间的流逝。 目前时间维护层主要负责维护以下几种类型的时间: 实时时间(CLOCK_REAL…

【Linux内核|时间子系统】Linux时间子系统(二)timekeeping简介

文章目录 1. timekeeping2. 计算墙上时间、启动时间差值3. timekeeper初始化3.1. 默认时钟源3.2. tk_setup_internals3.3. 设置时间 4. timekeeping_update4.1. tk_update_ktime_data:tkr_mono和tkr_raw设置4.2. update_fast_timekeeper4.3. shadow_timekeeper 1. t…

matlab 梯度下降 求偏导,通过计算图求梯度下降中各偏导的推导

通过计算图求梯度下降中各偏导的推导 Author: nex3z 2017-08-30 在 Neural Networks and Deep Learning 课程的 Logistic Regression Gradient Descent 一节以逻辑回归为例,介绍了使用计算图(Computation Graph)求梯度下降中各偏导的方法,但没有给出具体…

matlab|求导数/最值

本博文源于matlab求导数求极值求最值,涉及内容极限命令求导/diff求导/一元函数一阶导数,多阶导数,求参数导数/函数极值和最值/不给定区间求最值 用极限命令求导 例子:求函数3sinx4x^2在x0处的导数 >> syms t; limit((3*…

matlab 梯度下降 求偏导,吴恩达机器学习课程课时12梯度下降算法中参数θ0,θ1求偏导...

最近学习吴恩达的机器学习课程。 看到了线性回归的梯度下降算法。课程中将了一个非常简单的线性回归: 比如给出一些房子的size和对应的price,我们可以建立一个模型(在此模型就是线性回归), 希望之后在给出任意一个房子的size,可以…

matlab求COPULA偏导,matlab note

Update date:2018-01-05 Matlab Note 1 vine-copula 1.1 xlsread() 打开xlsx里面的数据 S3CE xlsread(电表建模数据.xlsx,S3CE,‘G2:G52’) 1.2常见希腊发音总结 image.png 3.prob() 连乘 由于matlab中的元素是以矩阵为单位,prod(x)就是把x向量中所有元…