线程的几种状态

(线程的几种状态)

1.线程状态(生命周期)

1.1.源码中的状态

关于Java线程的状态,网上说法很多,有五种、六种甚至七种,本文采用Java官方的线程状态分类。

实际上,官方一共给出了六种线程状态,我们来看看源码便可知:

public class Thread implements Runnable {
    /**
     * A thread state.  A thread can be in one of the following states:
     * <ul>
     * <li>{@link #NEW}<br>
     *     A thread that has not yet started is in this state.
     *     </li>
     * <li>{@link #RUNNABLE}<br>
     *     A thread executing in the Java virtual machine is in this state.
     *     </li>
     * <li>{@link #BLOCKED}<br>
     *     A thread that is blocked waiting for a monitor lock
     *     is in this state.
     *     </li>
     * <li>{@link #WAITING}<br>
     *     A thread that is waiting indefinitely for another thread to
     *     perform a particular action is in this state.
     *     </li>
     * <li>{@link #TIMED_WAITING}<br>
     *     A thread that is waiting for another thread to perform an action
     *     for up to a specified waiting time is in this state.
     *     </li>
     * <li>{@link #TERMINATED}<br>
     *     A thread that has exited is in this state.
     *     </li>
     * </ul>
     *
     * <p>
     * A thread can be in only one state at a given point in time.
     * These states are virtual machine states which do not reflect
     * any operating system thread states.
     *
     * @see #getState
     * @since 1.5
     */
    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         * <p>
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }
}
Copy

复制

从源码可以看出,Java语言定义了6种线程状态,作为内部枚举保存在Thread类中。这里我建议大家采用这六种状态,当然也可以按照自己的理解来。下面来解释一下每一种状态。

1.2.状态解释

在任意一个时间点,一个线程只能有且只有其中的一种状态,这6种状态分别如下:

1.新建(NEW):创建后尚未启动的线程处于这种状态。

2.运行(RUNNABLE):调用start()方法,RUNNABLE包括了操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间(该线程已经获取了除CPU资源外的其他资源,等待获取CPU 资源后才会真正处于运行状态)

官方为什么不将这两种状态分开呢?有种可能是:线程在这Running和Ready两种状态之间的时长太短了,现代cpu采用轮询式时间片调度,大部分线程Running和Ready的时间都非常短暂,因此考虑将这两种状态合并为RUNNABLE状态。

3.无限期等待(WAITING):处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。以下方法会让线程陷入无限期的等待状态:

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

4.限期等待(TIMED_WAITING):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。以下方法会让线程进入限期等待状态:

1.Thread.sleep(long millis)方法。 2.设置了Timeout参数的Object.wait()方法。 3.设置了Timeout参数的Thread.join()方法。 4.LockSupport.parkNanos()方法。 5.LockSupport.parkUntil()方法。

5.阻塞(BLOCKED):线程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待着获取到一个排他锁,这个事件将在另外一个线程获得锁的时候可能发生,比如synchronized之外;而“等待状态”则是在获得锁之后,主动释放锁,进入等待一段时间,或者等待唤醒动作的发生。

6.结束(TERMINATED):已终止线程的线程状态,线程已经结束执行。

补充: Java将操作系统中的运行和就绪两个状态合并称为运行状态。

阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,但是阻塞在java.concurrent包中Lock接口的线程状态却是等待状态,因为java.concurrent包中Lock接口对于阻塞的实现均使用了LockSupport类中的相关方法。

1.3.小结

状态名称 说明

NEW 初始状态,线程被构建,但是还没有调用start()方法

RUNNABLE 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统的称为“运行中”

BLOCKED 阻塞状态,表示线程阻塞于锁

WAITING 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)

TIMED_WAITING 超时等待状态,该状态不同于WAITING,它是可以在指定的时间自行返回的

TERMINATED 终止状态,表示当前线程已经执行完毕

2.线程状态转换

上述6种状态在遇到特定事件发生的时候将会互相转换,它们的转换关系如下图:

上图状态的转换和方法已经很明朗了,下面重点说说几种状态转换,以及相关方法补充。

2.1 进入等待/超时等待

2.1.1 进入等待状态

LockSupport.park()。

发生Io阻塞。

suspend(),方法已过时。

public final void wait() 释放锁

public final void join() 释放锁

2.1.1.1 wait方法的介绍

wait方法属于object类,wait()方法使当前线程暂停执行并释放锁,让其他线程可以进入synchronized数据块,当前线程被放入对象等待队列中。Wait()方法必须被包含在对应的synchronized语句中,无论是wait()方法还是notify()方法都需要获取目标对象的一个监视器。

当调用notify()方法后,将从对象的等待队列中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程才可能够获取锁标志;如果等待队列中没有线程,则notify()不起作用。notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。

被唤醒的线程并不会立即执行而是尝试获得锁,执行唤醒方法的线程也并不会立即释放锁。

2.1.1.2 join方法的介绍

join方法在内部使用wait()方法进行等待,底层用wait()来实现,可以释放锁。join方法上加了synchronuzed关键字,因此使用wait没有问题。

join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行,有些类似于同步的运行效果。在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。如果有多个线程,除了A线程之外的其他线程正常竞争cpu和锁。

join方法中如果传入参数,则表示这样的意思:如果A线程中调用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。(其实join()中调用的是join(0))。主线程中调用join,则主线程等待, 其他多个线程之间并不需要互相等待。

join()与synchronized的区别是:join在内部调用wait()方法进行等待,而synchronized关键字使用的是"对象监视器"原理作为同步。

/**
 * join方法
 * @throws InterruptedException
 */
public final void join() throws InterruptedException {
    //内部调用join(long millis)方法,参数为0 ,表示无限等待
    join(0);
}

/**
 * 等待millis毫秒
 * @param millis
 * @throws InterruptedException
 */
public final synchronized void join(long millis)
        throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    // millis等于0 表示无限期等待
    if (millis == 0) {
        while (isAlive()) {
            // 调用wait方法
            wait(0);
        }
    } 
    // 否则,限时等待
    else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            // 调用wait方法
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

复制

2.1.2 进入超时等待

public final void join(long millis) --超时等待 释放锁

LockSupport.parkNanos

LockSupport.parkUntil

wait(long timeout); --超时等待 释放锁

public static void sleep(long millis); —超时等待 不释放锁

2.1.2.1 sleep方法的介绍

sleep方法使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。sleep会让其他所有线程都有同等的cpu争夺权力!

睡眠时被中断,则会在sleep()处抛出InterruptedException 异常。

在调用Thread.sleep(long millis)时为millis 参数传递了一个负数, 则会抛出IllegalArgumentException 异常.

2.1.2.2 LockSupport类简介

LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,当然阻塞之后肯定得有唤醒的方法。

常用方法:

方法名称 描述

void park() 阻塞当前线程,如果调用unpark(Thread)方法或被中断,才能从park()返回。

void parkNanos(long nanos) 阻塞当前线程,超时返回,阻塞时间最长不超过nanos纳秒。

void parkUntil(long deadline) 阻塞当前线程,直到deadline时间点

void unpark(Thread) 唤醒处于阻塞状态的线程

park不需要获取某个对象的锁。因为中断的时候park不会抛出InterruptedException异常,所以需要在park之后自行判断中断状态,然后做额外的处理。

2.1.2.3 过期的suspend和resume方法

suspend方法被不推荐使用。不推荐使用suspend()去挂起线程的原因,是因为suspend()在导致线程暂停的同时,并不会去释放任何锁资源。其他线程都无法访问被它占用的锁。直到对应的线程执行resume()方法后,被挂起的线程才能继续,从而其它被阻塞在这个锁的线程才可以继续执行。如果resume()操作出现在suspend()之前执行,那么线程将一直处于挂起状态,同时一直占用锁,这就容易产生死锁。

而且,对于被挂起的线程,它的线程状态居然还是RUNNABLE

    // 创建子线程,在其内部调用suspend让其"阻塞"
    Thread thread = new Thread(() -> Thread.currentThread().suspend());
    thread.start();
    // 主线程睡眠三秒,让子线程充分运行
    Thread.currentThread().sleep(3000);
    // 获取子线程状态,发现还是RUNNABLE状态
    Thread.State state = thread.getState();
    System.out.println(state);

复制

2.2 进入RUNNABLE状态

  1. TIMED_WAITING状态结束
  2. WAITING状态被唤醒
  3. BLOCKED状态获得锁
  4. public static void yield();
  5. 线程的cpu时间片使用完毕还未执行完任务,重回就绪态,但此时不会释放锁。

2.2.1 yield方法的介绍

yield方法又称为“线程礼让”,使调用线程释放cpu执行权,让自己和其他多个线程重新争夺cpu执行权。

线程直接回到RUNNABLE状态,而不是让线程处于阻塞态,因此也有可能是当前让步的线程又进入到“运行状态”继续运行。

yield会尽量让同等级和高等级的线程具有更大的争夺权,而sleep会让所有线程都有同等的争夺权力,但它们并不是绝对的。毕竟java线程最终是调用操作系统的资源生成的,充满了不确定性。

yield方法不会释放持有已获得的锁,只是释放cpu的执行权。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

攻防世界-web-Confusion1

1. 题目描述 打开链接&#xff0c;如图 点击Login和Rigister&#xff0c;都报错 但是有提示 指出了flag所在的位置&#xff0c;题目中直接能获取到的信息暂时就这么些了 2. 思路分析 既然告诉了我们flag文件的位置&#xff0c;那么要读取到这个文件&#xff0c;要么是任意文…

合并区间问题

以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;intervals [[1,…

【Vue】自定义指令

hello&#xff0c;我是小索奇&#xff0c;精心制作的Vue系列持续发放&#xff0c;涵盖大量的经验和示例&#xff0c;如果对您有用&#xff0c;可以点赞收藏哈~ 自定义指令 自定义指令就是自己定义的指令&#xff0c;是对 DOM 元素进行底层操作封装 ,程序化地控制 DOM&#xff…

常见面试题-Redis持久化策略

谈谈Redis 的持久化策略&#xff1f; 参考文章&#xff1a; Redis 持久化机制演进与百度智能云的实践 Redis的确是将数据存储在内存的&#xff0c;但是也会有相关的持久化机制将内存持久化备份到磁盘&#xff0c;以便于重启时数据能够重新恢复到内存中&#xff0c;避免数据丢…

数据结构:顺序表

目录 一.顺序表 1.1概念以及结构 1.2动态顺序表实现 1.2.1文件创建&#xff1a; 1.2.2接口实现 1.顺序表打印 2.顺序表初始化 3.顺序表尾插 4.顺序表头插 5.顺序表尾删 6.顺序表头删 7.顺序表在pos位置插入x 8.顺序表删除pos位置的值 9.顺序表销毁 二.顺序表问题 一.…

关于 Docker

关于 Docker 1. 术语Docker Enginedockerd&#xff08;Docker daemon&#xff09;containerdOCI (Open Container Initiative)runcDocker shimCRI (Container Runtime Interface)CRI-O 2. 容器启动过程在 Linux 中的实现daemon 的作用 Docker 是个划时代的开源项目&#xff0c;…

「快学Docker」监控和日志记录容器的健康和性能

「快学Docker」监控和日志记录容器的健康和性能 1. 容器健康状态监控2. 性能监控3. 日志记录几种采集架构图 4. 监控工具和平台cAdvisor&#xff08;Container Advisor&#xff09;PrometheusGrafana 5. 自动化运维 1. 容器健康状态监控 方法1&#xff1a;需要实时监测容器的运…

在iPad pro上安装VSCode,秒变生产力工具提升编程工作效率!

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 本地环境配置二. 内网穿透2.1 安装cpolar内网穿透(支持一键自动安装脚本)2.…

智慧法院档案数字化解决方案

智慧法院档案数字化解决方案可以采用以下步骤&#xff1a; 1. 确定数字化目标&#xff1a;明确数字化的目标和范围&#xff0c;比如将所有的案件相关文件、纸质档案和材料进行数字化。 2. 确定数字化流程&#xff1a;制定数字化的流程和标准&#xff0c;比如采用哪些设备和软件…

人工智能基础_机器学习047_用逻辑回归实现二分类以上的多分类_手写代码实现逻辑回归OVR概率计算---人工智能工作笔记0087

然后我们再来看一下如何我们自己使用代码实现逻辑回归的,对二分类以上,比如三分类的概率计算 我们还是使用莺尾花数据 首先我们把公式写出来 def sigmoid(z): 定义出来这个函数 可以看看到这需要我们理解OVR是如何进行多分类的,我们先来看这个 OVR分类器 思想 OVR(One-vs-…

Android设计模式--模板方法模式

一&#xff0c;定义 定义一个操作中的算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 在面向对象的开发过程中&#xff0c;通常会遇到这样一个问题&#xff0c;我们知道一个算法所需的关键步…

2023年【上海市安全员C证】考试及上海市安全员C证找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年上海市安全员C证考试为正在备考上海市安全员C证操作证的学员准备的理论考试专题&#xff0c;每个月更新的上海市安全员C证找解析祝您顺利通过上海市安全员C证考试。 1、【多选题】2017年9月颁发的《中共上海市委…

达索系统SOLIDWORKS流体分析网格划分失败,大多是这2种原因

SOLIDWORKS Flow Simulation 是直观的流体力学 (CFD) 分析软件&#xff0c;该软件功能强大、操作人性化&#xff0c;快速轻松的分析产品内部或外部流体的流动情况&#xff0c;以用来改善产品性能和功能。 当流体分析运行网格划分时&#xff0c;提示失败。 这是由于凸起面与圆…

HTML新手入门笔记整理:HTML基本介绍

网页 静态页面 仅可供用户浏览&#xff0c;不具备与服务器交互的功能。 动态页面 可供用户浏览&#xff0c;具备与服务器交互的功能。 HTML HTML&#xff0c;全称HyperText Markup Language&#xff08;超文本标记语言&#xff09;,是一种用于创建网页的标准标记语言。用于…

基于向量加权平均算法优化概率神经网络PNN的分类预测 - 附代码

基于向量加权平均算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于向量加权平均算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于向量加权平均优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xf…

MySQL 事务的底层原理和 MVCC(二)

7.2. undo 日志 7.2.1. 事务回滚的需求 我们说过事务需要保证原子性&#xff0c;也就是事务中的操作要么全部完成&#xff0c;要么什么也不做。但是偏偏有时候事务执行到一半会出现一些情况&#xff0c;比如&#xff1a; 情况一&#xff1a;事务执行过程中可能遇到各种错误&a…

C语言--数组与指针--打印字符串的n种方式

一.知识背景 一维数组名的含义 arr一般表示数组的起始地址&#xff08;除了两种例外&#xff09; 1.在定义数组的同一个函数中(不是形参),求sizeof(arr),求整个数组的字节数 2.在定义数组的同一个函数中(不是形参),&arr1,加整个数组的大小 (经常考试) 3.除上面以外,arr都表…

io+day5

1&#xff0c;select服务端 1 #include<myhead.h>2 3 #define PORT 8888 //端口号4 #define IP "192.168.228.165" //IP地址5 6 7 int main(int argc, const char *argv[])8 {9 //1、创建用于接受连接的套接字10 int sfd socket(…

使用低代码可视化开发平台快速搭建应用

目录 一、JNPF可视化平台介绍 二、搭建JNPF可视化平台 【表单设计】 【报表设计】 【流程设计】 【代码生成器】 三、使用JNPF可视化平台 1.前后端分离&#xff1a; 2.多数据源&#xff1a; 3.预置功能&#xff1a; 4.私有化部署&#xff1a; 四、总结 可视化低代码…

java_函数式接口

文章目录 一、什么是函数式接口二、四大核心函数式接口三、使用举例 一、什么是函数式接口 如果一个接口只有一个抽象方法&#xff0c;那么该接口就是一个函数式接口函数式接口的实例可以通过 lambda 表达式、方法引用或者构造方法引用来创建如果我们在某个接口上声明了 Funct…