Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >掌握Java线程状态:从NEW到TERMINATED

掌握Java线程状态:从NEW到TERMINATED

作者头像
程序猿川子
发布于 2024-09-04 08:20:36
发布于 2024-09-04 08:20:36
1550
举报

本文的主要围绕着下面这个问题展开的,在阅读之前可以先自己思考一下问题的答案是什么?

  • 一般操作系统的线程状态都有哪些?
  • Java中的线程周期状态的生命周期状态都有哪些?
  • Java中线程状态是如何转化的?
  • Java中线程常用方法有哪些?

操作系统的线程状态

从操作系统的层面来说线程的状态划分为五种:初始状态、就绪状态、运行状态、阻塞状态和终止状态

初始状态(创建)

通过线程创建函数创建出来的新线程,在线程创建函数执行完后,将返回一个线程标识符供以后使用

就绪状态

操作系统中的线程被创建,可以分配CPU资源执行,但是还没有开始执行

运行状态

可运行状态的线程获得CPU资源以后,线程正在执行中,进入此状态

阻塞状态

指线程在执行中因某事件而受阻,处于暂停执行时的状态。此时线程会释放CPU资源,阻塞状态的线程没有机会获得CPU的使用权。

终止状态

线程执行完毕或者出现错误进入终止状态

iShot_2022-05-20_00.15.25.png
iShot_2022-05-20_00.15.25.png

Java中的线程周期状态

参考JDK18中java.lang.Thread.State,Java线程的状态可以划分一下几种:NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED

New(新建)

只是新创建出来的线程,还没有调用start()方法开始执行线程中的代码。

RUNNABLE(可运行)

这里的可运行状态(RUNNABLE)相当于操作系统线程状态中的就绪状态(READY)和运行状态(RUNNING)

抢占式调度系统会分配给就绪状态的线程一个时间片来执行任务。当时间片用完时,操作系统会根据优先级选择其他线程运行。一个线程在系统层面上可能是等待运行,也可能是正在运行。但是这些状态对JVM来讲,都可以看做可运行状态。

BLOCKED(阻塞)

该状态只与synchronized锁相关,WAITING,TIMED_WAITING状态下唤醒后因为需要竞争锁也会进入该状态

Java中的BLOCKED状态与操作系统中的阻塞状态不同,Java中的阻塞一定跟锁有关系。

从操作系统来说,线程因为调用阻塞API(如IO操作)会进入阻塞状态,在JVM下这个线程会是什么状态呢?不知道有没有大佬解释一下。 查询相关资料解释如下: 对JVM来说,等待CPU使用权(操作系统中线程处于可执行状态)和等待IO操作(操作系统中的线程处于休眠状态)没有区别,都是在等待某个资源,都被JVM认为是RUNNABLE状态。 所以是RUNNABLE状态?

WAITING(等待)

一个线程正在无期限等待另一个线程执行一个特定的动作唤醒此线程,被唤醒的线程会进入BLOCKED状态,重新竞争锁。

TIMED_WAITING(计时等待)

超时等待,让出CPU,不会无期限等待被其他线程唤醒。时间到了可以自动唤醒

TERMINATED(终止)

线程已经终止,可能是正常终止,也可能是异常终止,一般可以终止的操作如下所示:

  1. run()方法执行结束
  2. 线程执行抛出异常终止
  3. 对线程的实例调用stop()方法,现在该方法已经被废弃了。如果我们需要中断run()方法,可以调用interrupt()方法。

Java线程中的阻塞状态(BLOCKED)、无时限等待状态(WAITING)、有时限等待状态(TIMED_WAITING)都是一种状态,即通用线程生命周期中的休眠状态。也就是说,只要Java中的线程处于这三种状态时,那么,这个线程就没有CPU的使用权。

状态的转换

NEW到RUNNABLE状态

调用线程对象的start()方法

RUNNABLE与BLOCKED的状态转换

RUNNABLE转换为BLOCKED只有一种可能:要进入synchronized修饰的方法、代码块,却因为获取不到锁标志,所以变成了阻塞。

RUNNABLE与WAITING状态转换

  1. 获得synchronized隐式锁的线程,调用无参的Object.wait()方法
  2. 调用无参数的Thread.join()方法。例如,在线程A中调用线程B的join()方法,则线程A会等待线程B执行完以后再继续执行。而线程A在等待线程B执行的过程中,其状态会从RUNNABLE转换到WAITING。当线程B执行完毕,线程A的状态则会从WAITING状态转换成RUNNABLE状态。
  3. 调用LockSupport.park()方法,当前线程会阻塞,线程的状态会从RUNNABLE转换成WAITING。调用LockSupport.unpark(Thread thread)可唤醒目标线程,目标线程的状态又会从WAITING状态转换到RUNNABLE

RUNNABLE与TIMED_WAITING状态转换

基本上都是调用带有超时参数的方法,如下所示:

  1. 调用带超时参数的Thread.sleep(long millis)方法;
  2. 获得synchronized隐式锁的线程,调用带超时参数的Object.wait(long timeout)参数;
  3. 调用带超时参数的Thread.join(long millis)方法;
  4. 调用带超时参数的LockSupport.parkNanos(Object blocker, long deadline)方法;
  5. 调用带超时参数的LockSuppor.parkUntil(long deadline)方法

RUNNABLE到TERMINATED状态

  1. run()方法执行结束
  2. 线程执行抛出异常终止
  3. 对线程的实例调用stop()方法,现在该方法已经被废弃了。如果我们需要中断run()方法,可以调用interrupt()方法。

线程常见方法

Object类:wait(), notify(), notifyAll()

Thread类:start(), sleep(), yield(), join()

wait()方法

使用同步对象调用此方法,使当前线程处于等待状态,直到其他线程调用同步对象的notify()方法或 notifyAll() 方法唤醒线程,或者超过设置的超时时间。

方法的两个参数:

  • timeout - 等待时间(以毫秒为单位)
  • nanos - 额外等待时间(以纳秒为单位)

方法有几个注意点:

  1. 超时时间为timeoutnanos之和
  2. timeoutnanos参数都为 0,则不会超时,等同于wait()
  3. 调用方法前必须拥有对象的锁,否则会发生 IllegalMonitorStateException 异常
  4. wait() 方法会释放对象的“锁标志”,失去CPU使用权
  5. 调用wait(),wait(0),wait(0,0)方法后进入WAITIN状态
  6. 有参方法调用后,此线程进入TIMED_WAITING状态

notify()方法

使用同步对象调用此方法,从对象等待池中随机选一个线程移出并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,在当前线程放弃对该对象的锁定之前,被唤醒的线程将无法继续,它们随时准备争夺锁的拥有权。

  1. 调用方法前必须拥有对象的锁,否则会发生 IllegalMonitorStateException 异常
  2. 被唤醒的线程进入BLOCKED 状态,重新竞争锁

notifyAll()方法

使用同步对象调用此方法,唤醒对象等待池中所有的等待线程,让他们加入锁标志等待池中竞争锁。

  1. 调用方法前必须拥有对象的锁,否则会发生 IllegalMonitorStateException 异常
  2. 被唤醒的线程进入BLOCKED 状态,重新竞争锁

wait()notify()notifyAll() 只能在 synchronized 语句中使用,但是如果使用的是 ReenTrantLock 实现同步,该如何达到这三个方法的效果呢?解决方法是使用 ReenTrantLock.newCondition() 获取一个 Condition 类对象,然后 Conditionawait()signal() 以及 signalAll() 分别对应上面的三个方法。

start()方法

启动线程,使用线程的实例调用此方法,JVM会调用此线程的run方法。

  1. 调用方法后进入RUNNABLE状态,失去CPU使用权
  2. 不能多次启动同一线程实例;线程一旦结束,也不能重新启动。两者都会抛出 java.lang.IllegalThreadStateException 异常

sleep(long millis)方法

JDK18 描述如下所示:

Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors. 使当前执行的线程在指定的毫秒数内休眠(暂时停止执行),这取决于系统计时器和调度程序的精度和准确性。线程不会失去任何监视器的所有权。

  1. 这个方法需要传入参数,表示线程睡眠指定的时间
  2. 调用方法后进入TIMED_WAITING状态,失去CPU使用权
  3. 不会释放“锁标志”,如果有 synchronized 同步块,其他线程仍然不能访问共享数据
  4. 时间到了以后自动唤醒进入RUNNABLE状态

yield()方法

JDK18 描述如下所示:

A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint. Yield is a heuristic attempt to improve relative progression between threads that would otherwise over-utilise a CPU. Its use should be combined with detailed profiling and benchmarking to ensure that it actually has the desired effect. It is rarely appropriate to use this method. It may be useful for debugging or testing purposes, where it may help to reproduce bugs due to race conditions. It may also be useful when designing concurrency control constructs such as the ones in the java.util.concurrent.locks package.

此方法是一种尝试改变操作系统的线程调度的方法,调用此方法只是使当前线程重新回到可执行状态,该线程改变状态后可能会被马上执行。

  1. 调用方法后进入操作系统层面的就绪状态
  2. 不会释放“锁标志”
  3. yield() 方法只能使同优先级或者高优先级的线程得到执行机会

join()方法

JDK18 描述如下所示:

Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever. This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

A线程中调用B线程的join()方法,则A线程会等待B线程结束以后在继续执行。从源码实现以及jdk文档描述我们可以看出join是基于wait方法实现。

  1. 这个方法可以传入参数,参数为0时相当于无参调用
  2. join()join(0)相等,都是永远等待,调用方法后进入WAITIN状态
  3. 有参方法调用后,此线程进入TIMED_WAITING状态
  4. 会释放“锁标志”
  5. 对已经运行结束的线程调用join()方法会立刻返回

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java线程状态详解
最近在深入研究Java并发编程,看到网上有很多关于线程状态的总结,有的不全面,有的根本就是错的。因此,在这里我结合最权威的Java源代码,尝试对Java线程状态进行一个详细的解读。
张申傲
2020/09/03
9300
Java线程状态详解
Java线程状态(生命周期)以及线程状态转换详解
线程状态转换进入等待/超时等待进入等待状态进入超时等待LockSupport类简介过期的suspend和resume方法
用户7886150
2021/04/20
7100
Java线程的6种状态及切换(透彻讲解)
1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。 2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 3.阻塞(BLOCKED):表示线程阻塞于锁。 4.等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。 5.超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。6. 终止(TERMINATED):表示该线程已经执行完毕。
三哥
2019/03/04
1.1K0
Java线程的6种状态及切换(透彻讲解)
面试官:都说阻塞 I/O 模型将会使线程休眠,为什么 Java 线程状态却是 RUNNABLE?
使用 Java 阻塞 I/O 模型读取数据,将会导致线程阻塞,线程将会进入休眠,从而让出 CPU 的执行权,直到数据读取完成。这个期间如果使用 jstack 查看线程状态,却可以发现Java 线程状态是处于 RUNNABLE,这就和上面说的存在矛盾,为什么会这样?
andyxh
2019/10/08
1.6K0
面试官:都说阻塞 I/O 模型将会使线程休眠,为什么 Java 线程状态却是 RUNNABLE?
面试必答题“聊聊Java中线程的生命周期状态”如何破?
👆点击“博文视点Broadview”,获取更多书讯 “聊聊Java中线程的生命周期状态吧!” 这几乎是一道面试必答题,这道题怎么答才是最佳答案呢?本文就带大家来破解一下! 01 一张图说明线程生命周期 JVM源码中将线程的生命周期分为新建(New)、可运行(Runnable)、阻塞(Blocked)、等待(Waiting)、超时等待(Timed_Waiting)和终止(Terminated)这6种状态。 在系统运行过程中不断有新的线程被创建,老的线程在执行完毕后被清理,线程在排队获取共享资源或者锁时将被
博文视点Broadview
2022/07/04
3210
面试必答题“聊聊Java中线程的生命周期状态”如何破?
Java并发——线程状态 (二)
new Thread() 新建一个线程时,如果线程没有开始运行 start() 方法,所以也没有开始执行 run() 方法里面的代码,那么此时它的状态就是 New。而一旦线程调用了 start(),它的状态就会从 New 变成 Runnable
翰墨飘香
2024/02/23
2330
Java线程的几种状态
java.lang.Thread.State中定义的集中Java线程的状态: 1 /** 2 * A thread state. A thread can be in one of the following states: 3 * <ul> 4 * <li>{@link #NEW}<br> 5 * A thread that has not yet started is in this state. 6 * </li> 7 * <li>{@link
欠扁的小篮子
2018/04/10
1.4K0
Java线程的几种状态
面试题92:Java线程有几种状态?
Java线程一共分为6种状态,分别为: 新建状态(NEW) 就绪状态(RUNNABLE) 阻塞状态(BLOCKED) 等待状态(WAITING) 限时等待状态(TIMED_WAITING) 结束状态(TERMINATED) 我们也可以通过Tread中的State枚举查看这六种状态的定义: public enum State { /** * Thread state for a thread which has not yet started. */
爪哇缪斯
2023/05/10
2490
面试题92:Java线程有几种状态?
Java中线程的状态变化
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在API中java.lang.Thread.State这个枚举中给出了六种线程状态:
绿水长流z
2024/06/13
1610
Java中线程的状态变化
并发基础篇(四): 深入浅出java线程的状态
一、线程的五种状态 线程的生命周期可以大致分为5种,但这种说法是比较旧的一种说法,有点过时了,或者更确切的来说,这是操作系统的说法,而不是java的说法。但对下面所说的六种状态的理解有所帮助,所以也写出来,作为参考。 1. NEW: 线程的新建状态,是指通过New关键字创建了Thread类(或其子类)的对象。 2. RUNNABLE: 这种情况指的是Thread类的对象调用了start()方法,这时的线程就等待时间片轮转到自己这,以便获得CPU;第二种情况是线程在处于RUNNABLE状态时并没有运行完自
好好学java
2018/07/02
2850
多线程基础知识(全面):创建线程、线程状态如何变化、wait()、notify()、sleep()、停止线程
一直想着抽时间 学习多线程相关知识,目前整理了多线程的基础部分,特在此记录下,并发安全、线程池等后续再补充。
寻求出路的程序媛
2024/05/01
2790
多线程基础知识(全面):创建线程、线程状态如何变化、wait()、notify()、sleep()、停止线程
Java线程Thread的状态解析以及状态转换分析 多线程中篇(七)
在Thread类中有内部类 枚举State,用于抽象描述Java线程的状态,共有6种不同的状态
noteless
2019/03/04
9010
Java线程Thread的状态解析以及状态转换分析 多线程中篇(七)
【Java并发系列】Java线程基础
线程的runnable状态是从虚拟机的角度来看的,表示这个线程正在运行。但是处于Runnable状态的线程不一定真地消耗CPU。处于Runnable的线程只能说明该线程没有阻塞在java的wait或者sleep方法上,同时也没等待在锁上面。但是如果该线程调用了本地方法,而本地方法处于等待状态,这个时候虚拟机是不知道本地代码中发生了什么,此时尽管当前线程实际上也是阻塞的状态,但实际上显示出来的还是runnable状态,这种情况下是不消耗CPU的。
章鱼carl
2022/03/31
2690
【Java并发系列】Java线程基础
Java线程生命周期与状态切换
最近有点懒散,没什么比较有深度的产出。刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期、状态切换以及线程的上下文切换等等。编写本文的时候,使用的JDK版本是11。
Throwable
2020/06/23
8760
Java面试必考题之线程的生命周期,结合源码,透彻讲解!
在前面的几篇博客里,我们学习了Java的多线程,包括线程的作用、创建方式、重要性等,那么今天我们就要正式踏入线程,去学习更加深层次的知识点了。
JavaBuild
2024/05/27
1060
Java面试必考题之线程的生命周期,结合源码,透彻讲解!
线程的几种状态你真的了解么
我们来分析一下,当new Thread时,线程t1[Thread-0]状态为NEW。线程启动,执行run()方法,打印t1 running is false,t1将sleep,此时线程t1睡眠10000ms。然后主线程睡眠2000ms,变量running设置为false。这线程t1还在睡眠中。再将主线程睡眠2000ms,线程t1仍然在睡眠中。此时线程t1的状态为TIMED_WAITING。
Lvshen
2022/05/05
4010
线程的几种状态你真的了解么
【高并发】线程的生命周期其实没有我们想象的那么简单!!
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:
冰河
2020/10/29
3460
【高并发】线程的生命周期其实没有我们想象的那么简单!!
相关推荐
Java线程状态详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档