首页 > 其他分享 >【线程基础】【二】线程的状态流转

【线程基础】【二】线程的状态流转

时间:2023-04-18 21:59:16浏览次数:33  
标签:状态 流转 start WAITING 线程 就绪 等待

1  前言

上节我们看了线程是如何创建启动的,那么启动以后线程怎么管理呢,这就要看我们线程的状态管理了,这节我们就来看看线程都有哪些状态以及什么操作下会驱使状态的变化流转。

2  状态定义

Java线程有6种状态定义在Thread的子类State,分别是NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED

public enum State {
    // 初始
    NEW,
    // 可运行
    RUNNABLE,
    // 阻塞
    BLOCKED,
    // 等待
    WAITING,
    // 超时等待
    TIMED_WAITING,
    // 终止
    TERMINATED;
}
  • 初始(NEW):新创建了一个线程对象,但还没有调用start()方法时的状态。
  • 运行(RUNNABLE):Java线程中将操作系统中的就绪(ready)和运行中(running)两种状态笼统的称为“可运行”RUNNABLE状态。 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。所以调用了start()方法并不意味这线程会立即执行。
  • 阻塞(BLOCKED):表示线程阻塞于锁。没有获得锁的状态。等待的是其他线程释放锁。
  • 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断),无期限等待。等待被唤醒。
  • 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回,有时间期限的等待,等待一段设置好的时间。
  • 终止(TERMINATED):表示该线程已经执行完毕。线程正常的执行完毕,会进入到这种状态。或者是run()方法中出现了异常,线程会意外终止。

3  状态流转

那么接下里我们看看什么样的操作(即方法行为)下,线程的状态会发生变化:

3.1  初始状态

当创建一个线程后,线程就进入了初始状态。

3.2  可运行状态

3.2.1  就绪状态

就绪状态(ready)只是表名线程有资格运行,若调度程序没有挑选到此线程,此线程将永远是就绪状态。
怎样能够进入就绪状态?
(1)当调用线程的start()方法后,此线程即进入就绪状态。
(2)当线程执行完sleep()方法后,线程即进入就绪状态。
(3)其他线程执行Join()方法结束后,线程进入就绪状态。
(4)等待用户输入完毕,某个线程拿到对象锁,线程进入就绪状态。
(5)当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
(6)锁池里的线程拿到对象锁后,进入就绪状态。

3.2.2  运行中状态

运行中状态(running)即线程调度程序从可运行池中选择一个线程执行。
怎样能够进入运行中状态?只能由CPU选择任意一个就绪状态的线程执行,程序员无法手动指定运行哪个线程,这是线程进入运行中状态的唯一方式。

注意: 这里提到“选择哪个线程运行是CPU自己决定的”,这是Java官方给出的答案:The choice is arbitrary and occurs at the discretion of the implementation。译文:“这种选择是任意的,由执行者自行决定”。

 我们简单来看下示例:

public class ThreadTest extends Thread {
    @Override
    public void run() {
        System.out.println("运行Thread线程");
        for (int i = 0; i < 80; i++) {
        }
    }
    public static void main(String[] args) {
        ThreadTest threadClass = new ThreadTest();
        System.out.println("start前状态:" + threadClass.getState());
        threadClass.start();
        System.out.println("start后状态:" + threadClass.getState());
    }
}

3.3  阻塞状态

BLOCKED或WAITING或TIME_WAITING这三种统称为阻塞状态。因为这三种状态,都让线程进入等待状态,而不再是运行状态,所以统称为阻塞状态。
通俗的讲,就是线程在排队获取锁,类似排队结账要等着。

3.4  等待状态

等待状态即执行了wait()方法后,将锁释放,进入无尽的等待当中,直到被notify()方法唤醒。处于这种状态的线程CPU将不会给其分配执行时间,如果没有显式唤醒,将永远无期限的等待下去。

我们来看下阻塞和等待状态:

public class ThreadTest implements Runnable {

    @Override
    public void run() {
        say();
    }

    public synchronized void say() {
        try {
            TimeUnit.SECONDS.sleep(1);
            // 睡一秒
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadTest task = new ThreadTest();
        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

        thread1.start();
        thread2.start();
        // 线程1 获取到锁,还没到Sleep所以是RUNNABLE
        System.out.println("thread1状态:" + thread1.getState());
        // 线程2 获取不到锁,进入BLOCKED
        System.out.println("thread2状态:" + thread2.getState());

        TimeUnit.MILLISECONDS.sleep(200);
        // 线程1 TIMED_WAITING 因为sleep中
        System.out.println("thread1状态:" + thread1.getState());
        // 线程2 还是获取不到锁,还是BLOCKED
        System.out.println("thread2状态:" + thread2.getState());

        TimeUnit.SECONDS.sleep(2);
        // 线程1 wait方法 进入WAITING
        System.out.println("thread1状态:" + thread1.getState());
        // 线程2 获取到锁然后wait方法 进入WAITING
        System.out.println("thread2状态:" + thread2.getState());
    }
}

3.5  超时等待状态

等待状态跟超时等待状态的区别就是,超时等待可以设置一个超时时间,如果超过了这个时间,没有被唤醒,那就不等了,自动被唤醒。超时等待就是带时间的方法。我们不能控制的就不要纠结了,写代码要在边界内做事,我们可以优化代码,删除不必要的逻辑,来优化线程的执行速度。

3.6  终止状态

(1)当线程run()方法完成时,或者主线程的main()方法完成时,此时线程的状态就变成了终止状态,即便线程对象还存活着,还没有被垃圾回收,但是线程一旦终止,就不能复生。
(2)线程run()方法中抛出了异常,则会进入终止状态。

注意: 如果在一个终止后的线程重新调用start(),会抛出java.lang.IllegalThreadStateException异常,说明线程不能死而复生。

4  图示

接下来我们画个图来理解一下线程状态的流转:

(1)只能正序的三个状态
线程只能从NEW到RUNNABLE在到TERMINATED,这是顺序不能倒序。如果还想执行NEW,只能重新创建线程,原来的线程将被JVM回收。
(2)可以逆向执行的三个状态
线程的RUNNABLE到WAITING、RUNNABLE到TIME_WAITING、RUNNABLE到BLOCKED是可以双向执行的。
(3)线程状态不可跳跃执行
线程状态是不能跳跃的,NEW状态是不能跳跃到BLOCKED、WAITING、TIME_WAITING状态执行的,NEW也不能直接跳跃到TERMINATED状态。

5  小结

本节我们主要看了下线程的状态的定义以及状态流转,有理解不对的地方欢迎指正哈。

标签:状态,流转,start,WAITING,线程,就绪,等待
From: https://www.cnblogs.com/kukuxjx/p/17331031.html

相关文章

  • 【线程基础】【一】线程的创建方式
    1 前言本节开始我们来回顾下线程基础相关的东西,最近在复习所以来做一些笔记哈,这节我们来讲讲创建线程的方式。2 创建分类Java提供了两种线程的创建方法,第一种是继承Thread类;第二种是实现Runable接口,并将Runnable实例传递给Thread类。详细的可以参考官方文档哈:https://docs.......
  • vue全家桶进阶之路37:Vue3 状态管理
    Vue3的状态管理主要是通过Vuex4来实现。Vuex是一个专为Vue.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。在Vue3的状态管理中,以下是各个属性的作用:state:存储应用程序中的状态数据。它可以......
  • day12_响应状态码
    一、状态码大类状态码分类说明1xx响应中——临时状态码,表示请求已经接受,告诉客户端应该继续请求或者如果它已经完成则忽略它2xx成功——表示请求已经被成功接收,处理已完成3xx重定向——重定向到其它地方:它让客户端再发起一个请求以完成整个处理。4xx客户端......
  • 进程上下文切换、线程上下文切换、中断上下文切换的区别
    概念​CPU上下文切换指的是CPU从一个进程或线程切换到另一个进程或线程的次数。当CPU执行一个进程或线程时,会为其建立一个执行上下文(Context),当CPU切换到另一个进程或线程时,需要保存当前的上下文并建立新的上下文,这个过程就是上下文切换。​上下文切换会......
  • 基于LINUX系统下多线程贪吃蛇小游戏
    基于Ncurse图形库的C语言小游戏。涉及到,C变量,流程控制,函数,指针,结构体等知识内容,动态链表的创建和插入,以及释放。数组的遍历,#include<stdlib.h>#include<curses.h>#defineROW20#defineCOL20#defineROW_Snake2#defineCOL_Snake2#defineUP1#defineDOWN......
  • 一个Java线程的线生(线生 vs 人生)
    java线程的使用1.Java多线程概述下面我们看下Java的多线程作者:博学谷狂野架构师GitHub:GitHub地址(有我精心准备的130本电子书PDF)只分享干货、不吹水,让我们一起加油!......
  • Qt多线程之QMutex
    QMutex同一个QMutexlock()时,其他的lock()操作要等待locked的地方unlock()(可以保证顺序执行)测试代码:#include<iostream>#include<QThread>#include<QMutex>usingnamespacestd;classmyThread1:publicQThread{public:myThread1(int&num,QMutex&......
  • 线程委任出现冲突
    当我们在C#中编写委托事件进行传递参数来改变对象属性时,容易引起另一个线程拥有该对象的现象,这是线程之间的关联问题,要想解决此问题,我们就要将委托回来的线程对象和当前函数中的线程对象相关联。//将出现问题的代码放在此函数中Dispatcher.Invoke(newAction(......
  • sqlserver 从等待状态判断系统资源瓶颈
    一、相关视图2005、2008提供了以下三个视图供获取连接详细信息:DMV用处参考Sys.dm_exec_requests返回有关在SQLServer中执行的每个请求的信息,包括当前的等待状态sys.dm_exec_requests(Transact-SQL)-SQLServer|MicrosoftLearnSys.dm_exec_sessions对于每个通过身份验证的会......
  • asp.net signalR 专题—— 第三篇 如何从外部线程访问 PersistentConnection
       在前面的两篇文章中,我们讲到的都是如何将消息从server推向client,又或者是client再推向server,貌似这样的逻辑没什么异常,但是放在真实的环境中,你会很快发现有一个新需求,如何根据第三方系统的数据变化来即时的将新数据推送到各个客户端,比如下面这样:ok,原理就是上面的这......