Java进阶DAY32-03-JUC扩展 1 线程状态

1 线程状态

1.1 状态介绍

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢?Java中的线程

状态被定义在了java.lang.Thread.State枚举类中,State枚举类的源码如下:

public class Thread {
    
    public enum State {
    
        /* 新建 */
        NEW , 
​
        /* 可运行状态 */
        RUNNABLE , 
​
        /* 阻塞状态 */
        BLOCKED , 
​
        /* 无限等待状态 */
        WAITING , 
​
        /* 计时等待 */
        TIMED_WAITING , 
​
        /* 终止 */
        TERMINATED;
    
    }
    
    // 获取当前线程的状态
    public State getState() {
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }
    
}

通过源码我们可以看到Java中的线程存在6种状态,每种线程状态的含义如下

线程状态具体含义
NEW一个尚未启动的线程的状态。也称之为初始状态、开始状态。线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程象,没有线程特征。
RUNNABLE当我们调用线程对象的start方法,那么此时线程对象进入了RUNNABLE状态。那么此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不是立即得到执行,线程的运行与否要听令与CPU的调度,那么我们把这个中间状态称之为可执行状态(RUNNABLE)也就是说它具备执行的资格,但是并没有真正的执行起来而是在等待CPU的度。
BLOCKED当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
WAITING一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种,分别是调用Object.wait()、join()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。
TIMED_WAITING一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种,分别是:Thread.sleep(long),Object.wait(long)、join(long)。
TERMINATED一个完全运行完成的线程的状态。也称之为终止状态、结束状态

各个状态的转换,如下图所示:

 

🧠 理论理解

Java 为了精准描述线程的生命周期,引入了 Thread.State 枚举,一共定义了 6 种状态:NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED。这 6 个状态覆盖了线程从“创建”到“死亡”的全流程。

核心理解是:线程的状态不是单一存在的,而是随着程序的执行不断“流转”。比如,RUNNABLE 表示线程有资格运行但是否立即执行是由 CPU 调度器决定的;BLOCKED/WAITING 是线程主动放弃执行权;而 TIMED_WAITING(限时等待)能让线程“休息一会再回来”。

这 6 个状态是 Java 多线程并发控制的基础,掌握它们才能看懂线程 dump、排查死锁和定位性能瓶颈。


🏢 企业实战理解

  • 阿里巴巴:阿里在生产中常用 jstack 工具监控 JVM 线程状态,发现大量 BLOCKED 状态通常意味着锁竞争问题,而大量 WAITING 则可能是外部依赖阻塞(如远程调用超时)。

  • 字节跳动:在 IM 消息队列中,字节会监控所有工作线程的 RUNNABLE 比例,保证系统实时性。一旦发现过多 TIMED_WAITING 状态(比如 Thread.sleep 太多),就会优化逻辑。

  • Google / OpenAI:云计算平台会提供线程池监控页面,展示所有线程的实时状态(线程总数、阻塞线程、空闲线程),这依赖对 Thread.State 的深入掌握。

1.2 案例演示

为了验证上面论述的状态即状态转换的正确性,也为了加深对线程状态转换的理解,下面通过三个案例演示线程间中的状态转换。

1.2.1 案例一

本案例主要演示TIME_WAITING的状态转换。

需求:编写一段代码,依次显示一个线程的这些状态:NEW -> RUNNABLE -> TIME_WAITING -> RUNNABLE -> TERMINATED

为了简化我们的开发,本次我们使用匿名内部类结合lambda表达式的方式使用多线程。

代码实现

public class ThreadStateDemo01 {
​
    public static void main(String[] args) throws InterruptedException {
​
        //定义一个内部线程
        Thread thread = new Thread(() -> {
            System.out.println("2.执行thread.start()之后,线程的状态:" + Thread.currentThread().getState());
            try {
                //休眠100毫秒
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("4.执行Thread.sleep(long)完成之后,线程的状态:" + Thread.currentThread().getState());
        });
​
        //获取start()之前的状态
        System.out.println("1.通过new初始化一个线程,但是还没有start()之前,线程的状态:" + thread.getState());
​
        //启动线程
        thread.start();
​
        //休眠50毫秒
        Thread.sleep(50);
​
        //因为thread1需要休眠100毫秒,所以在第50毫秒,thread处于sleep状态
        //用main线程来获取thread1线程的状态,因为thread1线程睡眠时间较长
        //所以当main线程执行的时候,thread1线程还没有睡醒,还处于计时等待状态
        System.out.println("3.执行Thread.sleep(long)时,线程的状态:" + thread.getState());
​
        //thread1和main线程主动休眠150毫秒,所以在第150毫秒,thread早已执行完毕
        Thread.sleep(100);
​
        System.out.println("5.线程执行完毕之后,线程的状态:" + thread.getState() + "\n");
​
    }
​
}

控制台输出

1.通过new初始化一个线程,但是还没有start()之前,线程的状态:NEW
2.执行thread.start()之后,线程的状态:RUNNABLE
3.执行Thread.sleep(long)时,线程的状态:TIMED_WAITING
4.执行Thread.sleep(long)完成之后,线程的状态:RUNNABLE
5.线程执行完毕之后,线程的状态:TERMINATED

1.2.2 案例二

本案例主要演示WAITING的状态转换。

需求:编写一段代码,依次显示一个线程的这些状态:NEW -> RUNNABLE -> WAITING -> RUNNABLE -> TERMINATED

public class ThreadStateDemo02 {
​
    public static void main(String[] args) throws InterruptedException {
​
        //定义一个对象,用来加锁和解锁
        Object obj = new Object();
​
        //定义一个内部线程
        Thread thread1 = new Thread(() -> {
            System.out.println("2.执行thread.start()之后,线程的状态:" + Thread.currentThread().getState());
            synchronized (obj) {
                try {
​
                    //thread1需要休眠100毫秒
                    Thread.sleep(100);
​
                    //thread1100毫秒之后,通过wait()方法释放obj对象是锁
                    obj.wait();
                    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("4.被object.notify()方法唤醒之后,线程的状态:" + Thread.currentThread().getState());
        });
​
        //获取start()之前的状态
        System.out.println("1.通过new初始化一个线程,但是还没有start()之前,线程的状态:" + thread1.getState());
​
        //启动线程
        thread1.start();
​
        //main线程休眠150毫秒
        Thread.sleep(150);
​
        //因为thread1在第100毫秒进入wait等待状态,所以第150秒肯定可以获取其状态
        System.out.println("3.执行object.wait()时,线程的状态:" + thread1.getState());
​
        //声明另一个线程进行解锁
        new Thread(() -> {
            synchronized (obj) {
                //唤醒等待的线程
                obj.notify();
            }
        }).start();
​
        //main线程休眠10毫秒等待thread1线程能够苏醒
        Thread.sleep(10);
​
        //获取thread1运行结束之后的状态
        System.out.println("5.线程执行完毕之后,线程的状态:" + thread1.getState() + "\n");
​
    }
​
}

控制台输出结果

1.通过new初始化一个线程,但是还没有start()之前,线程的状态:NEW
2.执行thread.start()之后,线程的状态:RUNNABLE
3.执行object.wait()时,线程的状态:WAITING
4.被object.notify()方法唤醒之后,线程的状态:RUNNABLE
5.线程执行完毕之后,线程的状态:TERMINATED

1.2.3 案例三

本案例主要演示BLOCKED的状态转换。

需求:编写一段代码,依次显示一个线程的这些状态:NEW -> RUNNABLE -> BLOCKED -> RUNNABLE -> TERMINATED

public class ThreadStateDemo03 {
​
    public static void main(String[] args) throws InterruptedException {
​
        //定义一个对象,用来加锁和解锁
        Object obj2 = new Object();
​
        //定义一个线程,先抢占了obj2对象的锁
        new Thread(() -> {
            synchronized (obj2) {
                try {
                    Thread.sleep(100);              //第一个线程要持有锁100毫秒
                    obj2.wait();                          //然后通过wait()方法进行等待状态,并释放obj2的对象锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
​
        //定义目标线程,获取等待获取obj2的锁
        Thread thread = new Thread(() -> {
            System.out.println("2.执行thread.start()之后,线程的状态:" + Thread.currentThread().getState());
            synchronized (obj2) {
                try {
                    Thread.sleep(100);              //thread3要持有对象锁100毫秒
                    obj2.notify();                        //然后通过notify()方法唤醒所有在ojb2上等待的线程继续执行后续操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("4.阻塞结束后,线程的状态:" + Thread.currentThread().getState());
        });
​
        //获取start()之前的状态
        System.out.println("1.通过new初始化一个线程,但是还没有thread.start()之前,线程的状态:" + thread.getState());
​
        //启动线程
        thread.start();
​
        //先等100毫秒
        Thread.sleep(50);
​
        //第一个线程释放锁至少需要100毫秒,所以在第50毫秒时,thread正在因等待obj的对象锁而阻塞
        System.out.println("3.因为等待锁而阻塞时,线程的状态:" + thread.getState());
​
        //再等300毫秒
        Thread.sleep(300);
​
        //两个线程的执行时间加上之前等待的50毫秒总共是250毫秒,所以第300毫秒,所有的线程都已经执行完毕
        System.out.println("5.线程执行完毕之后,线程的状态:" + thread.getState());
​
    }
​
}
​
​
//---------------------代码简化--------------------------------------------
Object obj = new Object();
​
        Thread t1 = new Thread(()->{
            synchronized (obj){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
​
​
        Thread t2 = new Thread(()->{
            System.out.println("线程开启之后的状态" + Thread.currentThread().getState());
            synchronized (obj){
                System.out.println("进入之后的状态" + Thread.currentThread().getState());
            }
        });
​
​
​
        System.out.println("创建线程对象后,但是不调用start方法的状态" + t2.getState());
        t2.start();
        Thread.sleep(100);
        System.out.println(t2.getState());
        Thread.sleep(2000);
        System.out.println(t2.getState());

控制台输出结果

1.通过new初始化一个线程,但是还没有thread.start()之前,线程的状态:NEW
2.执行thread.start()之后,线程的状态:RUNNABLE
3.因为等待锁而阻塞时,线程的状态:BLOCKED
4.阻塞结束后,线程的状态:RUNNABLE
5.线程执行完毕之后,线程的状态:TERMINATED

通过上面3个案例的代码演示,我们可以证明开始章节说所述的线程状态以及线程状态转换都是正确的。

🧠 理论理解

线程状态是动态变化的,为了验证 Thread.State 的正确性,我们通过 3 个案例演示状态流转:

1️⃣ 案例一:从 NEWRUNNABLETIMED_WAITING(sleep 中) → RUNNABLETERMINATED

2️⃣ 案例二:从 NEWRUNNABLEWAITING(wait 中) → RUNNABLE(被 notify 唤醒) → TERMINATED

3️⃣ 案例三:从 NEWRUNNABLEBLOCKED(等待锁) → RUNNABLETERMINATED

核心点

  • TIMED_WAITING限时等待(比如 sleepwait(timeout))。

  • WAITING无限等待(比如 waitjoin)。

  • BLOCKED等待锁的状态(比如两个线程抢同一把锁)。


🏢 企业实战理解

  • 阿里巴巴:在 12306 高并发抢票中,阿里发现 BLOCKED 线程飙升是典型性能瓶颈,通过分析 jstack 的“阻塞状态”,调整了锁粒度,提升并发性能。

  • 字节跳动:在视频推荐系统中,字节监控 WAITING 线程比例,防止业务逻辑中线程“卡死”在 wait() 导致响应超时。

  • 腾讯:在微信支付中,TIMED_WAITING 状态是常态(如轮询场景),腾讯会结合 APM 工具采集线程池状态做健康检测。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值