线程是如何在 6 种状态之间转换的?

Java全能学习+面试指南:https://javaxiaobear.cn

今天我们主要学习线程是如何在 6 种状态之间转换的。

线程的 6 种状态

就像生物从出生到长大、最终死亡的过程一样,线程也有自己的生命周期,在 Java 中线程的生命周期中一共有 6 种状态。

  1. New(新创建)
  2. Runnable(可运行)
  3. Blocked(被阻塞)
  4. Waiting(等待)
  5. Timed Waiting(计时等待)
  6. Terminated(被终止)

如果想要确定线程当前的状态,可以通过 getState() 方法,并且线程在任何时刻只可能处于 1 种状态。

New 新创建

下面我们逐个介绍线程的 6 种状态,如图所示,首先来看下左上角的 New 状态。

New 表示线程被创建但尚未启动的状态:当我们用 new Thread() 新建一个线程时,如果线程没有开始运行 start() 方法,所以也没有开始执行 run() 方法里面的代码,那么此时它的状态就是 New。而一旦线程调用了 start(),它的状态就会从 New 变成 Runnable,也就是状态转换图中中间的这个大方框里的内容。

Runnable 可运行

Java 中的 Runable 状态对应操作系统线程状态中的两种状态,分别是 Running 和 Ready,也就是说,Java 中处于 Runnable 状态的线程有可能正在执行,也有可能没有正在执行,正在等待被分配 CPU 资源。

所以,如果一个正在运行的线程是 Runnable 状态,当它运行到任务的一半时,执行该线程的 CPU 被调度去做其他事情,导致该线程暂时不运行,它的状态依然不变,还是 Runnable,因为它有可能随时被调度回来继续执行任务。

阻塞状态

接下来,我们来看下 Runnable 下面的三个方框,它们统称为阻塞状态,在 Java 中阻塞状态通常不仅仅是 Blocked,实际上它包括三种状态,分别是 Blocked(被阻塞)、Waiting(等待)、Timed Waiting(计时等待),这三 种状态统称为阻塞状态,下面我们来看看这三种状态具体是什么含义。

Blocked 被阻塞

首先来看最简单的 Blocked,从箭头的流转方向可以看出,从 Runnable 状态进入 Blocked 状态只有一种可能,就是进入 synchronized 保护的代码时没有抢到 monitor 锁,无论是进入 synchronized 代码块,还是 synchronized 方法,都是一样。

我们再往右看,当处于 Blocked 的线程抢到 monitor 锁,就会从 Blocked 状态回到Runnable 状态。

Waiting 等待

我们再看看 Waiting 状态,线程进入 Waiting 状态有三种可能性。

  1. 没有设置 Timeout 参数的 Object.wait() 方法。
  2. 没有设置 Timeout 参数的 Thread.join() 方法。
  3. LockSupport.park() 方法。

刚才强调过,Blocked 仅仅针对 synchronized monitor 锁,可是在 Java 中还有很多其他的锁,比如 ReentrantLock,如果线程在获取这种锁时没有抢到该锁就会进入 Waiting 状态,因为本质上它执行了 LockSupport.park() 方法,所以会进入 Waiting 状态。同样,Object.wait() 和 Thread.join() 也会让线程进入 Waiting 状态。

Blocked 与 Waiting 的区别是 Blocked 在等待其他线程释放 monitor 锁,而 Waiting 则是在等待某个条件,比如 join 的线程执行完毕,或者是 notify()/notifyAll() 。

Timed Waiting 限期等待

在 Waiting 上面是 Timed Waiting 状态,这两个状态是非常相似的,区别仅在于有没有时间限制,Timed Waiting 会等待超时,由系统自动唤醒,或者在超时前被唤醒信号唤醒。

以下情况会让线程进入 Timed Waiting 状态。

  1. 设置了时间参数的 Thread.sleep(long millis) 方法;
  2. 设置了时间参数的 Object.wait(long timeout) 方法;
  3. 设置了时间参数的 Thread.join(long millis) 方法;
  4. 设置了时间参数的 LockSupport.parkNanos(long nanos) 方法和 LockSupport.parkUntil(long deadline) 方法。

讲完如何进入这三种状态,我们再来看下如何从这三种状态流转到下一个状态。

想要从 Blocked 状态进入 Runnable 状态,要求线程获取 monitor 锁,而从 Waiting 状态流转到其他状态则比较特殊,因为首先 Waiting 是不限时的,也就是说无论过了多长时间它都不会主动恢复。

只有当执行了 LockSupport.unpark(),或者 join 的线程运行结束,或者被中断时才可以进入 Runnable 状态。

如果其他线程调用 notify() 或 notifyAll()来唤醒它,它会直接进入 Blocked 状态,这是为什么呢?因为唤醒 Waiting 线程的线程如果调用 notify() 或 notifyAll(),要求必须首先持有该 monitor 锁,所以处于 Waiting 状态的线程被唤醒时拿不到该锁,就会进入 Blocked 状态,直到执行了 notify()/notifyAll() 的唤醒它的线程执行完毕并释放 monitor 锁,才可能轮到它去抢夺这把锁,如果它能抢到,就会从 Blocked 状态回到 Runnable 状态。

同样在 Timed Waiting 中执行 notify() 和 notifyAll() 也是一样的道理,它们会先进入 Blocked 状态,然后抢夺锁成功后,再回到 Runnable 状态。

当然对于 Timed Waiting 而言,如果它的超时时间到了且能直接获取到锁/join的线程运行结束/被中断/调用了LockSupport.unpark(),会直接恢复到 Runnable 状态,而无需经历 Blocked 状态。

Terminated 终止

再来看看最后一种状态,Terminated 终止状态,要想进入这个状态有两种可能。

  • run() 方法执行完毕,线程正常退出。
  • 出现一个没有捕获的异常,终止了 run() 方法,最终导致意外终止。

注意点

最后我们再看线程转换的两个注意点。

  1. 线程的状态是需要按照箭头方向来走的,比如线程从 New 状态是不可以直接进入 Blocked 状态的,它需要先经历 Runnable 状态。
  2. 线程生命周期不可逆:一旦进入 Runnable 状态就不能回到 New 状态;一旦被终止就不可能再有任何状态的变化。所以一个线程只能有一次 New 和 Terminated 状态,只有处于中间状态才可以相互转换。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在win10上安装配置Hadoop的环境变量

一、背景 在windows10系统中运行seatunnel 二、安装部署 2.1. 下载 Hadoop包 从 Apache Hadoop 官网下载最新版本的 Hadoop,版本号保持与服务端的Hadoop版本一致。 https://hadoop.apache.org/releases.htmlIndex of /apache/hadoop/core/hadoop-3.2.3/ 2.2. 解…

Java持久层框架:MyBatis介绍

MyBatis 概述 概述 MyBatis,是支持定制化 SQL 、存储过程和高级映射的优秀持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain …

巡检管理系统哪一款简单实用?如何解决传统巡检难题,实现高效监管?

在电力、燃气、水务等公共服务领域,线路巡检工作是保障公众安全、避免事故发生的重要环节。然而,传统的巡检方式存在一些显著的问题,可能会对公共安全和稳定运行产生不利的影响。为了解决这些问题,需要一种能够实现高效、精准的线…

【Godot】【BUG】4.x NavigationAgent 导航不生效

4.2.beta2 试了半天才发现原来默认只对第一个有导航的 TileMap 的第 1 层 生效,而我设置的导航层不是第一层,然后我新建了一个 TileMap 将导航的瓦片设置到这个 TileMap 上了,如图 这样就解决了问题,不用再修改默认设置的东西了&a…

Qt QDialog模式对话框传递数据给主窗口(主窗口->子窗口)

Qt工作笔记-QDialog模式对话框传递数据给主窗口_qt dialog-CSDN博客话不多说&#xff0c;上图&#xff1a;这里同样是采用了Qt的信号与槽机制。项目文件分布如下&#xff1a;代码如下&#xff1a;dialog.h#ifndef DIALOG_H#define DIALOG_H#include <QDialog>namespace U…

react 中获取多个input输入框中的值的 俩种写法

目录 1. 使用受控组件 2. 使用非受控组件 1. 使用受控组件 这是React中最常见的方法&#xff0c;每个输入框都与React组件的state相关联&#xff0c;并通过onChange事件来更新state。 代码示例&#xff1a; import React, { Component } from react;class MultipleInputExam…

1 如何入门TensorFlow

近年来人工智能的火爆吸引了很多人&#xff0c;网上相关的热门课程报名的人很多&#xff0c;但是坚持下去的人却少。那些晦涩的原理没有一定知识的积累很难能理解。 如果你对人工智能感兴趣&#xff0c;且想利用人工智能去实现某项功能&#xff0c;而不是对人工智能本身感兴趣&…

SpringBoot+微信小程序实现的酒店预订小程序系统 附带详细运行指导视频

文章目录 一、项目介绍二、项目介绍三、运行截图四、主要代码 一、项目介绍 项目演示地址&#xff1a;视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBoot微信小程序框架开发的酒店预订小程序系统。首先&#xff0c;这是一个前后端分离的项目&#xff0c;代…

【Linux】进程概念(上)

进程概念 一、冯诺依曼体系二、操作系统三、进程1. 基本概念2. 描述进程 - PCB3. 组织进程4. 查看进程&#xff08;1&#xff09;通过系统调用接口查看&#xff08;2&#xff09;通过 /proc 系统文件夹查看 5. 通过系统调用创建进程 - fork&#xff08;1&#xff09;初识 fork&…

Muse 2获取实时脑电数据

Muse 2获取实时脑电数据 之前转载了一篇知乎大佬汇总的采集Muse数据的博客&#xff0c;从亚马逊中国刷到了一个Muse 2&#xff0c;看了下不到2000块&#xff0c;于是果断下单。。。 历时一个月终于到了。。。 试用 需外网才能获取冥想音频资源&#xff0c;然后才能采集数据…

Linux 指令学习

Linux 指令学习 以此为记录&#xff0c;也方便自己日后查看回顾&#xff01; Linux命令基础格式 无论是什么命令&#xff0c;用于什么用途&#xff0c;在Linux中&#xff0c;命令有其通用的格式&#xff1a; command&#xff1a; 命令本身 options&#xff1a;[可选&#xf…

c++_learning-c++标准库STL和boost库

c的标准库 STL标准库&#xff1a;#include<iostream>&#xff1a;#include<iomanip>&#xff1a;#include<cstdlib>&#xff1a;#include<cmath>&#xff1a;#include<tuple>&#xff1a;利用可变参数模板&#xff0c;借助“递归继承”或“递归组…

Spring Boot + EasyUI 创建第一个项目(一)

创建一个Spring Boot和EasyUI相结合的项目。 一、构建一个Spring Boot项目 Spring Boot之创建一个Spring Boot项目&#xff08;一&#xff09;-CSDN博客 二、配置Thymeleaf Spring Boot Thymeleaf&#xff08;十一&#xff09;_thymeleaf 设置字体_人……杰的博客-CSDN博客…

论文阅读[51]通过深度学习快速识别荧光组分

【论文基本信息】 标题&#xff1a;Fast identification of fluorescent components in three-dimensional excitation-emission matrix fluorescence spectra via deep learning 标题译名&#xff1a;通过深度学习快速识别 三维激发-发射矩阵荧光光谱中的荧光组分 期刊与年份&…

数据结构: 红黑树

目录 1.红黑树概念 2.红黑树性质 3.调整 1.如果p和u都是红色&#xff0c;将其都改为黑色即可,然后向上调整 2.如果p红&#xff08;u黑/u不在&#xff09;&#xff0c;这时候左子树两红&#xff0c;于是给右子树一个红&#xff08;旋转变色&#xff09; 2.1右单旋 变色- …

栈和队列的C++模拟实现

一、栈stack 1.介绍&#xff08;库里面的文档介绍&#xff09; 1. stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。 2. stack是作为容器适配器被实现的&#xff0c;容器适配器即是对…

有效管理token,充分发挥ChatGPT的能力

目录 给提供了 Token 的计算工具,来理解一下Token的计算方式,网址如下: 窗口如下: 实际消耗 Token 数量为 59个,换算之后为2.1-2.2的比例,即一个汉字消耗2.12.2个Token, 再测一下英文的Token消耗,包含空格在内,一共52个英文字母,消耗Token 13个,正好对应13个单词,…

vue3入门级笔记

一.vue3的优势 二.使用create-create-vue搭建vue3项目 三.项目目录和关键文件 四.组合式API 1&#xff0c;setup的写法和执行时机 执行时机比beforeCreate还要早 setup函数中&#xff0c;获取不到this(this 是undefined) 数据 和 函数 &#xff0c;需要在 setup 最后 return&a…

策略模式在社会中的应用

文章目录 &#x1f31f; 如何将设计模式策略模式运用到社会当中&#x1f34a; 什么是策略模式&#x1f34a; 策略模式在社会中的应用&#x1f389; 1. 政治选举&#x1f389; 2. 商业竞争&#x1f389; 3. 教育培训 &#x1f34a; 策略模式的优缺点&#x1f389; 优点&#x1f…

类加载的过程总结以及双亲委派模型[JVM]

类加载过程 类一共有七个生命周期:加载->验证->准备->解析->初始化->使用->卸载 加载&#xff08;加载字节码文件&#xff0c;生成.class对象&#xff09; 加载是类加载的第一个阶段。 加载阶段的任务是在类文件从磁盘加载到内存中&#xff0c;通常是从cl…