【Java多线程】搞不懂Java线程的几种状态?本文带你快速了解~

专栏文章:操作系统
作者主页:https://blog.csdn.net/2301_77611317?type=blog

🦝前言

Java中线程的状态分为6种:初始状态(NEW)、就绪状态(RUNNABLE)、等待状态(WAITING)、超时等待(TIMED_WAITING)、阻塞状态(BLOCKED)、终止状态(TERMINATED)

这6种状态之间也存在着某种转换方式。

🐱一、初始状态(NEW)

初始状态下,系统已经创建好Thread对象,线程任务也相应地被确立,但是线程并没有被启动。

    public static void main(String[] args) throws InterruptedException {
        Thread main=Thread.currentThread();
        Thread t1 =new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println("t1线程的状态:"+t1.getState());
        t1.start();
        t1.join();
    }

运行结果:

t1线程的状态:NEW

🐱二、就绪状态(RUNNABLE)

就绪状态一般分为两个子状态:运行状态(RUNNING)、准备状态(READY),但是在Java中RUNNABLE 兼并两个子状态的属性

  • 运行状态:线程正在CPU上运行。

  • 准备状态:线程不在运行,处于准备随时被CPU调度运行的状态。

        public static void main(String[] args) throws InterruptedException {
            Thread main=Thread.currentThread();
            Thread t1 =new Thread(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            t1.start();
            System.out.println("t1线程的状态:"+t1.getState());
            t1.join();
        }
    

    运行结果:

    t1线程的状态:RUNNABLE
    

🐱三、等待状态(WAITING)

等待状态下,线程没有等待时限,会无限期地进行等待,直到被notify或者notifyAll方法唤醒。

    public static void main(String[] args) throws InterruptedException {
        Thread main=Thread.currentThread();
        Thread t1 =new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //在t1线程终止之前,main线程将一直处于WAITING状态!
            System.out.println("main线程的状态:"+main.getState());
        });
        t1.start();
        t1.join();
    }

运行结果:

main线程的状态:WAITING

🐱四、超时等待(TIMED_WAITING)

与等待状态相反,超时等待有一定的等待时限,时限一过线程会自动唤醒。

    public static void main(String[] args) throws InterruptedException {
        Thread t1 =new Thread(()->{
            try {
                //t1进入TIMED_WATING状态,1000毫秒之后自动唤醒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t1.start();
        Thread.sleep(10);
        System.out.println("t1线程的状态:"+t1.getState());
        t1.join();
    }

运行结果:

t1线程的状态:TIMED_WAITING

🐱五、阻塞状态(BLOCKED)

当进入到synchronized关键字修饰的方法或者代码块时,线程处于阻塞状态。通常认为,此状态与上文提到的WAITING状态和TIMED_WAITING状态都属于阻塞,只不过进入这三种状态的方式不同。

1、synchronized关键字修饰的代码块

//创建“锁”对象
 Object locker= new Object();
        Thread t1 =new Thread(()->{
            System.out.println("线程1执行");
            Thread T1=Thread.currentThread();
            //synchronized修饰代码块,括号内填"锁"对象名 -->locker
            synchronized (locker){
                System.out.println("线程1拿到“锁”后的状态:"+T1.getState());
            }
        });

2、synchronized关键字修饰的方法

    //synchronized关键字修饰的方法
    public static synchronized void func(){
        System.out.println("线程1拿到“锁” ");
    }
    public static void main(String[] args) throws InterruptedException{
      

        Thread t1 =new Thread(()->{
            System.out.println("线程1执行");
            func();    //调用被synchronized修饰的func方法
    });
        t1.start();
    }

🐱六、终止状态(TERMINATED)

终止状态下,内核中线程已经被销毁,但Thread对象还存在。

当线程run方法或者lambda表达式执行完毕,或者主线程main执行完毕时,线程就会进入终止状态。

    public static void main(String[] args) throws InterruptedException {
        Thread t1 =new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t1.start();
        t1.join();
        //在t1终止之后查询状态
        System.out.println("t1线程的状态:"+t1.getState());
    }

运行结果:

t1线程的状态:TERMINATED

🐱七、线程状态之间的转换

1、处于NEW状态下的线程,在主线程或其他线程中调用start()后转换为RUNNABLE状态。

        public static void main(String[] args) {
			Thread t1= new Thread(()->{
          		 System.out.println("线程1执行");
       		});
        //NEW状态
        System.out.println("线程1状态:"+t1.getState());
        //t1线程转为RUNNABLE状态
        t1.start();
        //RUNNABLE状态
        System.out.println("线程1状态:"+t1.getState());   
        }

运行结果:

线程1状态:NEW
线程1状态:RUNNABLE

2、处于RUNNABLE状态下的线程,调用wait或者join方法都会进入WAITING状态。

  • join方法:举个栗子,在主线程中调用t1.join()等待t1,主线程进入WAITING状态。
   //在主线程中调用join方法等待t1线程
	public static void main(String[] args) throws InterruptedException{
        Thread main=Thread.currentThread();
        Thread t1 =new Thread(()->{
            System.out.println("线程1执行");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("调用join后,main线程状态:"+main.getState());
        });
        t1.start();
        //RUNNABLE状态
        System.out.println("调用join前,main线程状态:"+main.getState());
        //调用join 使main线程转换WAITING状态
        t1.join();
    }

运行结果:

线程1执行
调用join前,main线程状态:RUNNABLE
调用join后,main线程状态:WAITING
  • wait方法:这个方法一般用在syncronized修饰的代码块或方法里。

    调用wait会发生两个事件:(1)线程被解锁 “锁” 对象 (2)被解锁的线程将会进入WAITING状态。

    注意:(1)调用的是“锁”对象的wait! (2)必须要抛出InterruptedException异常(try/catch语句或者throws)

    代码示例:

    public static void main(String[] args) throws InterruptedException{
        Object locker=new Object();
        Thread t1= new Thread(()->{
            System.out.println("线程1执行");
            synchronized (locker){
                try {
                    //解锁后 t1进入WAITING状态
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1线程被唤醒");
            }

        });
     t1.start();
     System.out.println("线程1 wait之前的状态:"+t1.getState());
     Thread.sleep(100);
     System.out.println("线程1 wait之后的状态:"+t1.getState());
     //注意notify必须在“锁”里被调用
     synchronized (locker) {
         //唤醒t1
         locker.notify();
     }
    }

运行结果:

线程1执行
线程1 wait之前的状态:RUNNABLE
线程1 wait之后的状态:WAITING
t1线程被唤醒

当然,wait也有重载,其参数列表与sleep相同(sleep看这篇文章),值得注意的是,wait如果加了时间参数,t1线程解锁后不会进入WAITING,取而代之的是TIMED_WAITING;不需要调用notify方法唤醒线程,过了时间线程自动唤醒。

    public static long start=0,end=0;
    public static void main(String[] args) throws InterruptedException{
        Object locker=new Object();
        Thread t1= new Thread(()->{
            System.out.println("线程1执行");
            synchronized (locker){
                try {
                    //wait前的时间戳
                    start=System.currentTimeMillis();
                    //解锁后 t1进入WAITING状态
                    locker.wait(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //wait后的时间戳
                end=System.currentTimeMillis();
                System.out.println("t1线程被唤醒");
                System.out.println("wait的等待时间:"+(end-start));
            }

        });
     t1.start();
     System.out.println("线程1 wait之前的状态:"+t1.getState());
     Thread.sleep(100);
     System.out.println("线程1 wait之后的状态:"+t1.getState());
     //注意不需要手动唤醒wait
//     synchronized (locker) {
//         locker.notify();
//     }
     t1.join();
    }

运行结果:

线程1执行
线程1 wait之前的状态:RUNNABLE
线程1 wait之后的状态:TIMED_WAITING
t1线程被唤醒
wait的等待时间:1012   //理论是等待1秒,但是由于唤醒后线程并不会立马进入RUNNING状态

3、当只存在一个“锁”对象,又有两个线程需要获取"锁"对象时,其中一个线程抢到锁,另一个线程则会进入BLOCKED状态;只有当前者释放了锁,后者才会退出BLOCKED状态。一般将这种事件成为“锁竞争”。

代码示例:

    public static void main(String[] args) throws InterruptedException{
        Object locker=new Object();
        Thread main=Thread.currentThread();
        Thread t1 =new Thread(()->{
            synchronized (locker){
                System.out.println("t1线程抢到锁了");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2 =new Thread(()->{
            Thread T2=Thread.currentThread();
            synchronized (locker){
                System.out.println("t2线程抢到锁了");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
        Thread.sleep(10);
            if (t1.getState() == Thread.State.BLOCKED) System.out.println("t1线程状态:" + t1.getState());
            if (t2.getState() == Thread.State.BLOCKED) System.out.println("t2线程状态:" + t2.getState());
        t1.join();
        t2.join();
    }

运行结果:

t1线程抢到锁了
t2线程状态:BLOCKED
t2线程抢到锁了

🦄总结

Java线程状态分为六种:NEW(初始状态)、RUNNABLE(就绪状态)、WAITING(等待状态)、TIMED_WAITING(超时等待)、BLOCKED(阻塞状态)、TERMINATED(终止状态)。

  • NEW转为RUNNABLE调用start方法;
  • RUNNABLE转WAITING可调用join或者wait两种无参数的方法;转TIMED_WAITING则调用有参数的方法;转BLOCKED则需要触发锁竞争的事件;
  • 调用notify或者notifyAll方法可以消除WAITING状态;而TIMED_WAITING状态下的线程超时会自动唤醒。

文章到这结束啦,感谢阅读~

如果你觉得文章写的还不错,记得点赞收藏评论三连~ ❤

img

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值