java线程状态流转

本文详细解析了JVM中的线程状态,重点讲解了BLOCKED、WAITING和TIMED_WAITING状态,以及wait和notify方法的正确使用,包括获取/释放监视器锁、虚假唤醒和状态流转示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要分析jvm中涉及的线程状态以及这些状态的流转关系


wait和notify方法的执行前提是调用该方法的线程必须拿到该对象的监视器锁
1、wait和notify时当前线程必须拥有这个对象的监视器,synchronized (obj)
2、调用wait方法,将会把当前线程放入该对象的监视器锁等待集中
3、wait和wait(long)一旦调用就会立即释放监视器锁,尽管是wait(long)也不会在wait结束后才释放,也是一开始就释放
4、虚假唤醒问题。线程也可以在没有通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。
如果条件不满足,则继续等待。换句话说,等待应该总是出现在循环中
5、notify方法唤醒没有顺序
6、notify和notifyAll方法调用后,不会立即释放锁,也就意味着不会立即唤醒某个或全部线程,而是等当前线程释放锁后,这一点和wait方法相反
7、wait和sleep的异同:
两者都会让出cpu时间片,线程处于waiting状态
前者是Object的final方法,后者是Thread的静态本地方法
在同步语句中,wait方法会立即释放锁进入waiting状态;而sleep不会释放锁


一、jvm线程状态总览

点开Thread类中的内部枚举类Thread#State可以见注释,jvm中的线程状态主要分为以下六种:
在这里插入图片描述

	NEW				(还没有启动的线程)
	RUNNABLE		(在jvm中执行)
	BLOCKED			(被阻塞,等待获取监视器锁)
	WAITTING		(无限期地等待另一个线程执行特定操作,如notify)
	TIMED_WAITTING	(等待另一个线程执行操作长达指定等待时间)
	TERMINATED		(已退出)

一个线程在同一时刻有且只能有一种状态,该状态属于jvm虚拟机范畴内的状态,不反映任何操作系统层面的线程状态。

二、状态流转

这6中线程状态中,我们主要关注以下三种状态:BLOCKED、WAITTING、TIMED_WAITTING。
在这里插入图片描述

BLOCKED

等待锁的时候会被阻塞,获取到锁后变为RUNNABLE状态

WAITTING

当线程调用wait方法、join方法以及park方法后进入挂起状态,相应的调用notify方法以及unpark方法后恢复为RUNNABLE状态。

TIMED_WAITTING

与WAITTING状态基本一致,区别在于挂起的状态有超时时间。

需要注意,RUNNABLE状态实际细分为ready和running状态,线程刚start时不会直接获取到cpu的时间片执行任务,而是处于就绪状态(ready)被统一放入就绪队列中,等待着jvm的线程调度器进行调度,只有真正被调度起来才是running状态

三、wait-notify的正确搭配使用方式

还是参考Object类的源码:

wait方法:
在这里插入图片描述

notify方法:
在这里插入图片描述

可见,wait和notify方法的执行前提是调用该方法的线程必须拿到该对象的监视器锁,否则必定会抛出IllegalMonitorStateException异常,这是jvm的规定。

那么wait和notify方法配合使用的正确打开方式如下:

public class TestThreadState {
    public static void main(String[] args) throws InterruptedException {

        final long SLEEP = 1L;
        final Object lock = new Object();

        new Thread(() -> {
            long start = System.currentTimeMillis();
            synchronized (lock) {
                try {
                    System.out.println("线程1获取到锁");
                    TimeUnit.SECONDS.sleep(SLEEP);
                    System.out.println("线程1即将调用wait方法,挂起当前线程并释放锁");
                    // 执行wait方法,挂起当前线程并释放锁
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程1执行耗时:" + (System.currentTimeMillis() - start));
        }).start();

        // 让线程1获取到时间片获取到监视器锁(注意这里和线程1睡眠保持了一致)
        TimeUnit.SECONDS.sleep(SLEEP);

        new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("线程2获取到锁");
                    TimeUnit.SECONDS.sleep(3);
                    lock.notify();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

执行结果打印:

线程1获取到锁
线程1即将调用wait方法,挂起当前线程并释放锁
线程2获取到锁
线程1执行耗时:4015

执行代码可以看到流程为:
1、线程1调用wait方法使得当前线程挂起并释放监视器锁
2、线程2这时候获取到锁,进入同步块执行业务,结束后调用notify方法唤醒其他线程并退出同步块,释放监视器锁。
3、线程1恢复到RUNNABLE状态


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值