JavaEE线程的状态

Posted 保护小周ღ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaEE线程的状态相关的知识,希望对你有一定的参考价值。

哈喽,大家好~我是保护小周ღ,本期为大家带来的是 Java 多线程的 线程的状态, New 新建状态,Runnable 运行状态,Blocked 阻塞状态,waiting 等待状态,Time_Waiting 超时等待状态,Terminated 终止状态,以及展示线程的状态的常用方法。
更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘

一、线程的状态

表示线程当前所处的一个情况 ,线程有6 种状态,下文会详细讲述;

  1. New 新建状态:线程还没出创建,只有Thread 实例化的对象,调用start 方法之前的状态。

  1. Runnable 运行状态:被系统调度后,CPU 正在执行的,Ready 就绪态,系统调度,随时准备被CPU 执行。

  1. Blocked 阻塞状态:线程遇到某种机制之后不参与 CPU的调度执行,等待某个条件的触发唤醒线程

  1. waiting 等待状态: 无限期等待(死等)

  1. Time_Waiting 超时等待状态:设置了最大等待时间,超出最大等待时间就换醒线程

  1. Terminated 终止状态:线程生命周期结束后的状态

1.1 举例理解

关于线程的状态博主举个例子理解一下:

1. 张三和李四(两个线程)是邻居,他们打算一起去银行办理业务,首先他们两个到银行后进行挂 号操作(new 新建状态),随后他们两个人在大厅的 排队等待呼叫(线程 READY就绪状态)
2. “请 102号顾客来 1号窗口办理业务 —— 张三),当银行呼叫张三来1 号窗口办理业务我们可以看作是线程的调度,1号窗口我们认为是 CPU 的核心处理单元, 为张三办理业务(处理线程),张三听到呼叫后,马上来到1号柜台, 柜台小姐姐正式为张三办理业务(线程的运行状态)
3. 张三话不多说直接银行卡身份证递给小姐姐,张口就是我要取5 万块钱,小姐姐让张三输入密码,此时张老铁将银行卡密码忘记了,尝试了几次都不能成功,于是就给老婆打电话询问,结果显示正在通话中,无奈之下,只能感到很无奈,匆匆下线, 那么导致业务办理不成功(线程堵塞)的原因是:忘记了密码。
4. 因为张三忘记了密码,暂时无法获取密码(老婆电话通话中),无法继续处理业务,所以陷入阻塞等待(可以联系上老婆询问密码),所以银行呼叫了下一个用户,请 103号顾客来 1号窗口办理业务 —— 李四),此时银 行1号窗口可以看作是并行或并发的处理多线程,李四来处理业务就是从就绪状态到运行状态,李四张口就来,我要取 50万,柜台小姐姐说很抱歉暂时无法为您办理业务,根据规定大额取款需要提前三天预约,这边只能为您办理预约业务,您三天后可以来取,此时李四线程陷入了( 等待状态,但是具有时效性 ,线程需要等3天就可以继续执行了),三天后李四的业务就可以继续处理。
5. 这边处理李四业务的时候,张三老铁终于拨通了电话, 询得了密码,那么张三老铁又可以(此时忽略挂号)重新排队(张三获得密码,就从阻塞状态中唤醒,来到就绪状态准备被CPU 调度处理),请 104号顾客来 1号窗口办理业务 —— 张三,伴随着银行的呼叫,张三再次被调度,根据询问的密码,输入密码时提示成功,张老铁欣喜若狂,结果 柜台小姐姐告知银行存款不足,无法取出相应的5万元金额(无法办理取钱业务),需要等待银行余额充裕后方可继续进行取钱,所以张三再次陷入等待状(线程等待)。
6. 当银行继续处理几个存钱业务后金额足够(线程等待结束条件),再 呼叫张三来排队取钱,将张三从等待状态中唤醒,张三就继续排队(就绪),银行窗口办理业务(运行),取钱成功,线程结束。

1.2 阻塞、等待状态的同异

等待、阻塞可以看作不参加 CPU 的调度了,系统也不会安排该状态的线程被CPU 执行,两者的区别是:进入等待状态是线程主动的,而进入阻塞状态是被动的。

阻塞等待:

线程阻塞指一个线程在执行过程中暂停,等待某个条件的触发。一般会发生在线程加锁的情况下(synchronized),例如:两个线程同时对一片内存空间的数据进行增删查改,线程之间又是随机调度,很容易造成数据的误差问题,此时我们需要对该事务进行加锁操作(synchronized),什么意思,多线程之间并发执行,对事物加锁后,当一个线程对事物进行处理的时候,不允许其他线程对该事物进行操作,那么竞争事物的这个线程就会进行阻塞等待,直到先运行的线程对事物执行完毕。

运行的线程执行wait()方法,JVM(java虚拟机)会把该线程放入等待池中。wait() 方法:释放当前对象锁,然后使该线程进入等待状态“死等”(带参数的版本可以指定最大等待时间),不带参数的wait() 方法 需要其他线程使用notify() 方法来唤醒调用 wait() 方法的线程,当然 wait() 方法是建立在有锁对象的情况下(synchronized)。等到线程安全的章节博主再详细介绍

同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中,举个例子:两个线程同时处理一个“事务”势必会造成数据的安全问题,。


线程等待:

线程等待:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep() 线程休眠需要执行休眠时间,join() 方法线程等待,例如 : main 线程 调用 t.join 方法,意思就是 mian 线程需要等待 t 线程执行完毕后再执行自己,“死等”线程串行执行,join() 提供了一个带参数的版本可以指定最大等待时间,线程等待一般发生在主动的使线程进入等待状态。

BLOCKED 表示等待获取锁, WAITING 和 TIMED_WAITING 表示等待其他线程发来通知.

TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在无限等待唤醒


二、展示线程的常用方法

2.1 Thread.getState() 方法显示线程的状态

public static void main(String[] args) throws InterruptedException 
        Thread t = new Thread( () -> 
           while (true) 
               try 
                   Thread.sleep(1500);
                catch (InterruptedException e) 
                   e.printStackTrace();
               
               synchronized (Thread.currentThread()) 
                  System.out.println("线程 T执行");
                  try 
                      Thread.currentThread().wait(); // 返回当前线程引用
                   catch (InterruptedException e) 
                      e.printStackTrace();
                  
              
           
        );
        t.start(); //启动
        System.out.println(t.getState()); // 此时是主线程在判断 t 线程的状态,二者并无影响 Runnable

        t.sleep(1000); //t 线程休眠一秒 Time_Waiting
        System.out.println(t.getState());

        t.sleep(1000); //等待 t 线程调用 wait() 方法进入死等状态
        System.out.println(t.getState());
 

2.2 isAlive 方法判定线程的存活状态

isAlive() 方法,可以认为是处于不是 NEW 和 TERMINATED 的状态都是活着的

2.3 yield() 主动结束CPU 调度执行

让出 CPU的执行时间片, yield 不改变线程的状态, 但是会让调用的线程由运行状态重新进入就绪(排队)状态,二者都是属于运行状态,只不过是CPU 并发执行线程,有人正在执行就有人准备被执行。

 public static void main(String[] args) throws InterruptedException 
        Thread t = new Thread( () -> 
            int i = 0;
            while (true) 
                i++;
                System.out.print("t 线程执行");
                if((i + 1 )% 10 == 0) 
                    System.out.println();
                
                Thread.currentThread().yield(); //让出CPU 执行时间片
            
        );

        t.start(); //启动

        int i = 0;
        while (true) 
            System.out.print("main 线程执行");
            i++;
            if((i + 1 )% 10 == 0) 
                System.out.println();
            
        
    

由此可见本来是两个线程并发执行,但是由于 t 线程调用 yiele()方法后主动让出 CPU 的时间片,所以多数情况下是 main 线程在执行。


至此,Java 线程的状态,博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。

本期收录于博主的专栏——JavaEE,适用于编程初学者,感兴趣的朋友们可以订阅,查看其它“JavaEE基础知识”。

下期预告:线程安全相关问题

感谢每一个观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘

JavaEE初阶多线程 _ 基础篇 _ Thread类的使用线程的几个重要操作和状态

☕导航小助手☕

    🍱写在前面

        🧇一、Thread类的常见构造方法

        🍚二、Thread 的几个常见属性

        🍛三、和线程相关的几个重要的操作

                    🍞🍞3.1 启动线程 - start()

                    🍣🍣3.2 中断线程

                    🍤🍤3.3 等待线程 - join()

                    🥩🥩3.4 获取到线程引用

                    🧀🧀3.5 休眠线程 - sleep()

        🍜四、线程的状态

                    🍰🍰4.1 Java 线程中的基本状态

                    🥡🥡4.2 线程之间的状态是如何转换的


写在前面

这篇博客,仍然来介绍关于多线程基础篇的知识~

其主要介绍的内容是:Thread类 的常见构造方法和属性、和线程相关的几个重要操作 以及 Java 线程中的几种状态~

下面,正文开始 ......

一、Thread类的常见构造方法

构造方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable对象 创建线程
Thread(String name)创建线程对象,并命名
Thread(Runnable target)使用 Runnable对象 创建线程,并命名
【了解】Thread(ThreadGroup group,Runnable target) 线程可以被用来分组管理,分好的组即为线程组,这个目前我们了解即可

Thread():

默认无参构造方法,如 :Thread t1 = new Thread();


Thread(Runnable target):

使用 Runnable 创建一个任务,再把 任务 放到线程里面,

如 Thread t2 = new Thread(new MyThread());


Thread(String name):

给线程起一个名字,线程 在操作系统内核里 没有名字,只有一个身份标识~

但是 Java中 为了让程序猿 调试的时候方便理解,这个线程是谁~

就在 JVM 里给对应的 Thread对象 加了个名字(JVM中的 Thread对象 和 操作系统内核里面的线程 一一对应)~

这个名字对于程序的执行没影响,但是对于程序猿调试来说还是挺有用的~

如果不手动设置,也会有默认的名字,形如 Thread-0、Thread-1、......


Thread(Runnable target):

使用 Runnable对象 创建线程,并命名:

package thread;

public class Demo7 
    public static void main(String[] args) 
        Thread t = new Thread(new Runnable() 
            @Override
            public void run() 
                while (true) 
                    System.out.println("hello thread");
                    try 
                        Thread.sleep(1000);
                     catch (InterruptedException e) 
                        e.printStackTrace();
                    
                
            
        ,("我的线程"));
        t.start();

        while(true)
            System.out.println("hello main");
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
    

程序运行,我们可以找到 jconsole.exe 去看一看:

二、Thread 的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台进程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

getId():获取到的是 线程在 JVM 中的身份标识~

线程中的身份标识是有好几个的:内核的 PCB 上有标识;到了用户态线程库里 也有标识(pthread,操作系统提供的线程库);到了 JVM 里又有一个标识(JVM Thread类 底层也是调用操作系统的 pthread 库) ~

三个标识各不相同,但是目的都是作为身份的区分~

由于 介绍的是 Java程序,所以我们只需要知道 JVM 中的身份标识即可~


getName():

在 Thread 构造方法里,自己所起的名字~


getState():

PCB 里面有状态,此处得到的状态是 JVM 里面设立的状态体系,这个状态比操作系统内置的状态要更丰富一些~


getPriority():

获取到优先级~


isDaemon():

daemon 称为 "守护线程",也可以理解为 "后台线程"~

类似于 手机app,打开一个app,此时这个app就来到了 "前台",当按到 "回到菜单" 这样的按钮,此时app就切换到 "后台"~
线程也分成 前台线程 和 后台线程(这个是可以自己来设置的),创建线程出来默认为是 前台线程,前台线程 会阻止进程结束;换句话说,进程会保证所有的前台线程都执行完了 才会退出~

当然,main 这个线程就是一个前台线程~

后台线程不会阻止进程结束,所以 进程退出的时候 不关后台进程是否执行完 就退出了~

如:

package thread;

public class Demo7 
    public static void main(String[] args) 
        Thread t = new Thread(new Runnable() 
            @Override
            public void run() 
                while (true) 
                    System.out.println("hello thread");
                    try 
                        Thread.sleep(1000);
                     catch (InterruptedException e) 
                        e.printStackTrace();
                    
                
            
        ,("我的线程"));
        t.start();
    

执行结果:

分析:

由于该线程是一个前台线程,所以需要等待 其运行结束,进程才会结束,所以会一直执行下去~

再如:我们可以把他手动设置成 后台线程:

package thread;

public class Demo7 
    public static void main(String[] args) 
        Thread t = new Thread(new Runnable() 
            @Override
            public void run() 
                while (true) 
                    System.out.println("hello thread");
                    try 
                        Thread.sleep(1000);
                     catch (InterruptedException e) 
                        e.printStackTrace();
                    
                
            
        ,("我的线程"));
        
        //手动设置成 后台线程
        t.setDaemon(true);

        t.start();

    

运行结果:

分析:

通过 setDaemon(true) 可以把线程设置为后台线程,等到主线程执行完,进程就结束了~

需要注意的是,先要 设置线程,然后再启动线程~


示例:

package thread;

public class Demo8 
    public static void main(String[] args) 
        Thread t = new Thread(() ->
            while (true) 
                try 
                    Thread.sleep(1000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        ,"我的线程");

        t.start();

        System.out.println("线程Id:" + t.getId());
        System.out.println("线程名称:" + t.getName());
        System.out.println("线程状态:" + t.getState());
        System.out.println("线程优先级:" + t.getPriority());
        System.out.println("是否后台线程(true/false):" + t.isDaemon());
        System.out.println("是否存活:" + t.isAlive());
    

 运行结果:

这些操作都是获取到的是 一瞬间的状态,而不是持续性的状态~ 

三、和线程相关的几个重要的操作

3.1 启动线程 - start()

创建 Thread实例,并没有真的在操作系统内核里创建出线程(仅仅只是在安排任务,"跑步时 的 '各就各位,预备' ")!!!

而是 调用 start,才是真正创建出线程("发令枪响")!!!

这个在上面和上一篇的博客也提到过,这里就不做过多演示了~

3.2 中断线程

怎么让线程执行结束,其实方法很简单:只要让线程的 入口方法 执行完了,线程就随之结束了~

主线程 的入口方法,就可以视为 mian方法~

入口方法:其实就是 上一篇博客所介绍的 创建线程 的代码~

🚪上一篇博客的传送门🚪

所谓的 "中断线程",就是让线程尽快把 入口方法执行结束~


方法一:直接自己定义 标志位 来区分线程是否要结束~

package thread;

public class Demo9 
    //用一个布尔变量来表示 线程是否要结束
    //这个变量是一个成员变量,而不是局部变量
    private static boolean isQuit =  false;
    public static void main(String[] args) throws InterruptedException 
        Thread t = new Thread(() -> 
            while (!isQuit) 
                System.out.println("线程运行中......");
                try 
                    Thread.sleep(1000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
            System.out.println("新线程执行结束!");
        );
        t.start();

        Thread.sleep(5000);
        System.out.println("控制新线程退出!");
        isQuit = true;
    

运行结果:

这个代码中,控制线程结束,主要是这个线程里有一个循环,这个循环执行完,就算结束了~

很多时候创建线程都是 让线程完成一些比较复杂的任务,往往都有一些循环,正是因为有循环,执行的时间才可能比较长;如果线程本身执行的很快,刷的一下就结束了,那么也就没有提前控制它的必要了~ 


方法二:使用 Thread类 中自带的标志位~ 

这种方法是可行的~

Thread 其实内置了一个标志位,不需要咱们去手动创建标志位

Thread.currentThread().isInterrupted()

--currentThread() 是 Thread类的静态方法,获取到当前线程的实例,这个方法中有一个线程会调用它
--线程1 调用这个方法,就能返回线程1 的 Thread对象;
--线程2 调用这个方法,就能返回线程2 的 Thread对象~
--类似于 JavaSE 里面的 this~

--isInterrupted() 为判定内置的标志,可以获取到标志位的值,为 true 表示线程要被中断~
package thread;

public class Demo10 
    public static void main(String[] args) throws InterruptedException 
        Thread t = new Thread(() ->
            while (!Thread.currentThread().isInterrupted()) 
                System.out.println("线程运行中......");
                try 
                    Thread.sleep(1000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        );
        t.start();

        Thread.sleep(5000);
        System.out.println("控制新线程退出!");
        t.interrupt();
    

运行结果:

说明:

调用了 interrupt,产生了一个异常~

异常虽然出现了,但是线程仍然在继续运行~ 

注意理解 interrupt方法 的行为:

  1. 如果 t 线程 没有处在阻塞状态,此时 interrupt 就会修改内置的标志位~
  2. 如果 t 线程 正在处于阻塞状态,此时 interrupt 就让线程内部产生阻塞的方法,例如 sleep 抛出异常~

上述循环代码中,正好异常被捕获了~

而捕获之后,啥也没有干 只是打印了一个调用栈:

  

而如果 把上述代码中的  e.printStackTrace(); 给注释掉,那么就啥也不打印,运行结果调用栈也不打印了,直接忽略异常了~

正是因为这样的捕获操作,程序员就可以自行控制线程的退出行为了:

即 在里面可以自主的微操作了~

 


当然,除了 Thread.currentThread().isInterrupted(),Thread类还自带了一个静态方法 interrupted() 也可以访问标志位~

使用 Thread.interrupted() 即可~

  1. 如果当前线程处于运行的状态,就是修改标志位
  2. 如果当前线程处于阻塞的状态,则是触发一个异常,线程内部就会通过这个异常被唤醒

这里也不做过多叙述了,一般掌握一个就可以了~

3.3 等待线程 - join()

我们已经知道,线程之间的执行顺序完全是随机的,看系统的调度~

一般情况下写代码,其实是比较讨厌这种随机性的,更需要能够让顺序给确定下来~

join() 就是一种确定线程执行顺序的 辅助手段~

如:咱们不可以确定两个线程的开始执行顺序,但是可以通过 join() 来控制两个线程的结束顺序~

如:上一篇博客曾举了一个例子:

 private static void concurrency() 
        //concurrency 的意思是 "并发"
        long begin = System.currentTimeMillis();
        Thread t1 = new Thread(() ->
            int a = 0;
            for(long i = 0; i < count; i++) 
                a++;
            
        );

        Thread t2 = new Thread(() ->
            int a = 0;
            for(long i = 0; i < count; i++) 
                a++;
            
        );

        t1.start();
        t2.start();

        try 
            t1.join();
            t2.join();
         catch (InterruptedException e) 
            e.printStackTrace();
        

        long end = System.currentTimeMillis();
        System.out.println("多线程并发执行的时间:" + (end-begin) + "毫秒");

    

我们知道,main、t1、t2 三个线程是随机调度执行的~

但是此处的需求是,希望让 t1、t2 都执行完了之后,main再进行计时,来统计执行时间~

就是希望 t1、t2 先执行完,main 后执行完~

咱们无法干预 调度器的行为(调度器还是该咋随机咋随机),但是可以让 main 线程进行等待~

就在 main 里分别调用了 t1.join() 和 t2.join() ~

t1.join() —> main阻塞,等待 t1 执行完~

t2.join() —> main阻塞,等待 t2 执行完~ 

当 t1、t2 都执行完了以后,main 解除阻塞,然后才能继续往下执行,才能执行完~

main 阻塞了,就不参与调度了,但是 t1、t2 仍然参与调度,它们的执行还是会 随机调度、交替执行的~ 


main 有点特殊,不太方便 join()~

一般情况下,想让谁阻塞,谁就调用 join() 即可 ~

如:要实现让 t2 等待 t1,main 等待 t2,就可以 main 去调用 t2.join(),t2 调用 t1.join() 即可~

如:

package thread;

public class Demo11 
    private static Thread t1 = null;
    private static Thread t2 = null;

    public static void main(String[] args) throws InterruptedException 
        System.out.println("main begin");
        t1 = new Thread(() -> 
            System.out.println("t1 begin");
            try 
                Thread.sleep(3000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            System.out.println("t1 end");
        );
        t1.start();

        t2 = new Thread(() -> 
            System.out.println("t2 begin");
            try 
                t1.join();
             catch (InterruptedException e) 
                e.printStackTrace();
            
            System.out.println("t2 end");
        );
        t2.start();

        t2.join();
        System.out.println("main end");
    

运行结果:


如:

package thread;
//控制 main 先运行 t1,t1 执行完 再执行 t2
public class Demo12 
    public static void main(String[] args) throws InterruptedException 
        System.out.println("main begin");
        Thread t1 = new Thread(() -> 
            System.out.println("t1 begin");
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            System.out.println("t1 end");
        );
        t1.start();

        //等待 t1 结束
        t1.join();

        Thread t2 = new Thread(() -> 
            System.out.println("t2 begin");
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            System.out.println("t2 end");
        );

        t2.start();
        t2.join();
        System.out.println("main end");
    

运行结果:


join() 的行为:

  1. 如果被等待的线程 还没执行完,就阻塞等待~
  2. 如果被等待的线程 已经执行完了,就直接返回~

当然,join() 还有其他的版本~

方法说明
public void join() 等待线程结束(死等)
public void join(long millis) 等待线程结束,最多等 millis 毫秒(设定了等待的最大时间)
public void join(long millis, int nanos) 同理,但可以更高精度(设定了等待的最大时间)

在实际的开发过程中,在一般情况下,都不会使用 "死等" 的方式~

因为 "死等" 的方式有风险~

万一代码出了 bug 没控制好,就很容易让服务器 "卡死",无法继续工作~

更多的情况下是 等待的时候预期好最多等多久,超过时间了就需要做出一些措施~

3.4 获取到线程引用

为了对线程进行操作,就需要获取到线程的引用~

这里的操作,就是指:线程等待、线程中断、获取各种线程的属性~

如果是 继承 Thread类,重写 run方法,可以直接在 run 中使用 this 即可获取到线程的实例~

但是如果是 Runnable 或者 Lambda,this 就不行了(指向的就不是 Thread实例)~

更通用的方法是,使用 Thread.currentThread() ,currentThread() 是一个静态方法,其特点是 哪个线程来调用这个方法,得到的线程就是哪个实例~

3.5 休眠线程 - sleep()

sleep() 能够让线程休眠一会儿~

前面已经所介绍了 sleep() 的使用方法,现在来画图介绍一下 sleep() 到底是如何使用的~


 注意:实际上应该是 双向链表 连接,不过为了简单,所以画的就是 单向链表 了~

注意:

如果写了一个 sleep(1000),那么也不一定 就会在1000ms 之后就上 CPU 运行~

1000ms 之后只是把这个 PCB 放回了就绪队列!!!至于说这个线程啥时候执行,还得看调度器的心情~

因此,sleep(1000) 意味着休眠时间不小于 1000ms,实际的休眠时间会略大于 1000ms,这个误差精度在 10ms 以下~ 

四、线程的状态

4.1 Java 线程中的基本状态

操作系统中 进程的状态 有 阻塞状态、就绪状态、执行状态~

而在 Java/JVM里 线程中也有一些状态,更是对此做出了一些细分~

New:安排了工作,还未开始行动,创建了 Thread对象,但是还没有调用 start方法,系统内核里面没有线程~

Runnable:就绪状态,包含了两个意思:

  1. 正在 CPU上运行
  2. 还没有在 CPU 上运行,但是已经准备好了

Blocked:等待锁~

Waiting:线程中调用了 wait方法~

Time Waiting:线程中通过 sleep方法 进入的阻塞~

Terminated:工作完成了,系统里面的线程已经执行完毕,销毁了(相当于线程的 run方法 执行完了),但是 Thread对象 还在~

 Blocked、Waiting、Time Waiting 三种状态都是 阻塞状态,只不过是产生阻塞状态的原因不一样,Java里面使用不同的状态来表示了~


package thread;

public class Demo13 
    public static void main(String[] args) throws InterruptedException 
        Thread t = new Thread(() -> 
                System.out.println("hello thread");
                try 
                    Thread.sleep(1000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
        );

        //在 t.start() 之前获取,获取到的是 线程还未创建的状态
        System.out.println(t.getState());

        t.start();
        t.join();

        //在 t.join() 之后获取,获取到的是线程已经结束之后的状态
        System.out.println(t.getState());
    

运行结果:

4.2 线程之间的状态是如何转换的

主干道是:New => Runnable => Terminated ~

在 Runnable状态 会根据特定的代码进入支线任务,这些 "支线任务" 都是 "阻塞状态"~

这三种 "阻塞状态",进入的方式不一样,同时阻塞的时间也不同,被唤醒的方式也不同~

如:sleep() 等到时间到了自动唤醒,至于 wait() 和 synchronized() 是如何唤醒的以后会介绍的~

这一篇的博客就到此为止了,下一篇博客将会介绍到 多线程安全的问题 ~

如果感觉这一篇博客对你有帮助的话,可以一键三连走一波,非常非常感谢啦 ~

以上是关于JavaEE线程的状态的主要内容,如果未能解决你的问题,请参考以下文章

JAVA语言中请写出线程从阻塞状态恢复到就绪状态的三种途径

Thread 类的基本用法JavaEE

Java线程和多线程(十五)——线程的活性

Java线程和多线程(十五)——线程的活性

javaee需要学那些东西

Java并发基础Java线程的生命周期