在Java并发编程的面试中,多线程的问题几乎是必问的,而有一个送分题但却最容易因为细节而丢分——“请说说Java线程的生命周期”。

  大多数人都能脱口而出‘新建,运行,阻塞,死亡’,但这其实是操作系统的线程状态,而不是Java语言层面的答案,在Java对于线程引用提供的方法 getState( ) 的枚举中,线程其实还有更细节的六大状态

还有观察线程各种状态的小工具——jconsole(Java Monitoring and Management Console(Java 监控与管理控制台))

接下来,我们一一介绍:

一、NEW

安排了工作,但还未开始行动

new出了实例对象,但还未start(),我们可以通过代码观察:

public class demo1 {
    public static void main(String[] args) {
       Thread t1 = new Thread(()->{
           System.out.println("1");
       });
        System.out.println(t1.getState());
        t1.start();

    }
}

发现 t1.getState() 的结果为 NEW

二、RUNNABLE

可工作的,又可以分为正在工作中或即将开始工作

我们设计一个死循环,循环体内什么都不执行,但线程处于死循环内本身也是一种执行,所以我们预期 t1.getState( ) 的结果为RUNNABLE

代码实例:

public class State {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
           while(true){

           }
        });

        t1.start();
        System.out.println(t1.getState());
    }
}

三、TERMINATED

工作已完成

设计一串代码,保证计算机可以快速执行这个线程到完毕,在线程start后,我们sleep(3000)睡个一秒来保证线程执行完毕,随后通过 t1.getState( ) 来观察打印结果

代码实例:

public class State {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            System.out.println("1");
        });

        t1.start();
        Thread.sleep(3000);
        System.out.println(t1.getState());
    }
}

四、TIMED_WAITING

线程此时正处于一种阻塞状态,这个线程正在排队中,非死等,阻塞的时间有上限

我们设计一个死循环,循环体内加上一条 Thread.sleep(1000),线程每执行一次循环体内的内容就会阻塞1000毫秒,而计算机的执行速度非常之快,可能执行循环体内的内容只需要几毫秒甚至更短,所以绝大部分时间都因为 sleep 的缘故处于一种阻塞状态,所以我们简单设计的代码绝

大概率是能通过 t1.getState( ) 来得到想要的 TIMED_WAITING的

代码实例:

public class State {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
           while(true){
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });

        t1.start();
        Thread.sleep(1000);
        System.out.println(t1.getState());
    }
}

我们设计start后,先让主线程睡一秒,再观察t1线程的状态,如果不睡这一秒,主线程会在t1.start之后迅速观察并打印t1线程的状态,t1线程此时的状态大概率还未第一次执行到sleep,所以我们很可能会观察到RUNNABLE,所以我们设计先睡上这一秒以便我们观察

五、WAITING

线程此时处于一种阻塞状态,正在排队中,而且是一种没有时间上限的死等,永远处于阻塞状态

这次我们不观察t1的状态,而是这样设计:让t1线程执行死循环代码,让main线程死等t1线程执行完后再执行自身代码,此时观察main线程的状态

代码实例:

Thread main = Thread.currentThread();
Thread t1 = new Thread(()->{
   while(true){
       try {
           Thread.sleep(1000);
       } catch (InterruptedException e) {
           throw new RuntimeException(e);
       }
   }
});

t1.start();
t1.join();
System.out.println(main.getState());

❌️❌️❌️ 如果main要等t1线程执行完,可能这辈子都无法在编译软件里看到main的状态了,因为t1.join( ) 是main线程对t1线程的无限的等待,永远无法执行到打印main线程的状态。

✅️✅️✅️ 我们通过 jdk 的bin目录中的一个名为 jconsole.exe 的软件来观察线程的状态,什么是jconsole?全名:Java Monitoring and Management Console(Java 监控与管理控制台)

这是我们观察线程状态的好工具

我们点开后选中并连接代码所在的类State中,选择左上角的线程

我们观察main线程的状态——WAITING,正是我们想要的结果✅️

⚠️如果是第一次用需要注意⚠️:

这里没有t1线程的名字是因为我们的引用叫t1,但线程的名字不是t1,我们没有给这个线程取名字,所以编译器默认给这个线程取名叫 Thread-0——这就是我们的t1线程,我们可以点一下看看

上述我们的TIMED_WAITING讲到,t1线程绝大部分时间处于sleep(1000)这1000毫秒里,所以t1线程处于一种有时间上限的等待的阻塞状态,我们也发现其状态确实如此✅️

更多推荐