聊聊线程到底有几种状态

109 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

介绍

线程到底有几种状态?

网上主要分为两种说法:

五种状态

  1. 新建状态(New)
  2. 就绪状态(Runnable)
  3. 运行状态(Running)
  4. 阻塞状态(Blocked)
  5. 死亡状态(Dead)

六种状态

  1. New(新创建)
  2. Runnable(可运行)
  3. Blocked(被阻塞)
  4. Waiting(无限期等待)
  5. Time waiting(限期等待)
  6. Terminated(已终止)

那么,到底哪种方法是对的,或者说更加权威。我们翻开源码看看线程到底有哪几种状态。

image.png

可以看到,源码里Thread类中还有个State枚举类表示线程的状态。总共有New、Runnable、Blocked、Waiting、Time waiting、Terminated六种状态。

接下来,我们就详细聊一下这六种状态。

新建状态

当我们创建一个线程时,这个线程就是处于新建状态。

Thread thread = new Thread(() -> {
    System.out.println("线程开始执行");
});

可运行状态

当线程调用start方法的时候,线程就会处于可运行状态

thread.start();

其实可运行状态包含两部分

  • 可运行状态
  • 运行状态

当线程调用了start(),此时线程是处于可运行状态,但不一定是运行状态;只有当CPU时间片真正被线程占用,执行线程的run方法,线程才会变成运行状态。

被堵塞状态

线程无法获取对象锁时的状态。也就是说此时线程可能还是占用这CPU时间片,但是由于对象锁,线程无法往下执行,堵塞在了这里。

有两个线程thread1,thread2。

  • thread1和thread2都会去执行testBlocked方法
  • 但是testBlocked有对象锁
  • 如果当thread1先获取到锁,那么thread2执行堵塞住,等待thread1执行完释放锁
  • 但thread2可能还是拥有CPU时间片的,它只是在等待锁的释放而已。
public static void main(String[] args) {
    Thread thread1 = new Thread(ThreadTest::testBlocked);

    Thread thread2 = new Thread(ThreadTest::testBlocked);

    thread1.start();
    thread2.start();
}

public static synchronized void testBlocked(){
    System.out.println(Thread.currentThread().getName() + "执行了。。。");
    try {
        Thread.sleep(10000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

无限期等待状态

线程进入了等待CPU时间片的阶段,并且是没有时间限制的。

thread.wait();

执行线程的wait方法时,线程会释放CPU资源;只有当其他线程调用notify/notifyAll时,这个线程才会被唤醒,继续参与到CPU资源的争夺。


Thread t1 = new Thread(() -> System.out.println("1"));
Thread t2 = new Thread(() -> {
    try {
        t1.join();
        System.out.println("2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

执行线程t1的join方法时,t2线程会进入等待状态,直到t1线程执行完成,否则t2线程会一直等待下去。

限期等待状态

线程进入了等待CPU时间片的阶段,但是有时间限制的,当达到了限期时间,线程会继续加入到CPU时间片的争夺。

Thread.sleep(5000L);

执行sleep方法后,线程会释放CPU进入等待状态,直到达到了过期时间。

Thread t1 = new Thread(() -> System.out.println("1"));
Thread t2 = new Thread(() -> {
    try {
        t1.join(1000L);
        System.out.println("2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

执行线程t1的join方法时,t2线程会进入等待状态,直到t1线程执行完成,或者等到达到了过期时间,才会结束等待状态

终止状态

有三种方式都会导致线程的终止状态

  • 线程任务正常结束
  • 线程任务异常结束
  • 调用stop方法停用线程

线程正常结束

线程的run方法正常执行完成,线程就会变为终止状态

线程异常结束

线程内部程序执行报错

程序报错,导致线程异常终止

Thread thread3 = new Thread(() -> System.out.println(1 / 0));
thread3.start();

堵塞/等待状态的线程执行interrupt()方法

由于调用了sleep方法,线程进入了等待状态。此时,调用interrupt()方法会导致报错,抛出InterruptedException异常,线程异常终止

Thread thread = new Thread(ThreadTest::testBlocked);

thread.start();

thread.interrupt();

public static synchronized void testBlocked(){
    System.out.println(Thread.currentThread().getName() + "执行了。。。");
    try {
        Thread.sleep(5000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

调用stop方法停用线程

stop方法是用来强行终止线程,它会释放子线程所持有的的所有锁,会导致共享数据的不一致性,是一种很危险的方式,所以不推荐使用。

最后简单画个图总结下

1666945598948.png

文中如有不足之处,欢迎指正!一起交流,一起学习,一起成长 ^v^