56.【线程状态】

1. 线程状态描述

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在APljava.lang.Thread.State这个枚举中给出了六种线程状态:

这里先列出各个线程状态发生的条件,下面将会对每种状态进行详细解析

线程状态导致状态发生条件
NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。
Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep、Object.wait
Teminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

我们不需要去研究这几种状态的实现原理,我们只需知道在做线程操作中存在这样的状态。那我们怎么去理解这几个状态呢,新建与被终止还是很容易理解的,我们就研究一下线程从Runnable(可运行)状态与非运行状态之间的转换问题。

在这里插入图片描述

2. Time Waiting (计时等待)

Timed Waiting在API中的描述为:一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。单独的去理解这句话,真是玄之又玄,其实我们在之前的操作中已经接触过这个状态了,在哪里呢?

在我们写卖票的案例中,为了减少线程执行太快,现象不明显等问题,我们在run方法中添加了sleep语句,这样就强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。

其实当我们调用了sleep方法之后,当前执行的线程就进入到“休眠状态”,其实就是所谓的Timed Waiting(计时等待),那么我们通过一个案例加深对该状态的一个理解。

实现一个计数器,计数到5,在每个数字之间暂停1秒,每隔2个数字输出一个字符串

代码:

package test.demo11;

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <= 5; i++) {
            if(i % 2 == 0) {
                System.out.println("-------" + i);
            }
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new MyThread().start();
    }
}

结果:

-------0
0
1
-------2
2
3
-------4
4
5

通过案例可以发现,sleep方法的使用还是很简单的。我们需要记住下面几点:

  1. 进入TIMED_WAITING状态的一种常见情形是调用的sleep方法,单独的线程也可以调用,不一定非要有协作关系。

  2. 为了让其他线程有机会执行,可以将``Thread.sleep()的调用放在线程run()`之内。这样才能保证该线程执行过程中会睡眠

  3. sleep与锁无关,线程睡眠到期自动苏醒,并返回到Runnable(可运行)状态。

    sleep中指定的时间是线程不会运行的最短时间。因此,sleep方法不能保证该线程睡眠到期后就开始立刻执行。

Timed Waiting 线程状态图:

在这里插入图片描述

3. Blocked(锁阻塞)

Blocked状态在API中的介绍为:一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态。

我们已经学完同步机制,那么这个状态是非常好理解的了。比如,线程A与线程B代码中使用同一锁,如果线程A获取到锁,线程A进入到Runnable状态,那么线程B就进入到Blocked锁阻塞状态。

这是由Runnable状态进入Blocked状态。除此Waiting以及Time Waiting状态也会在某种情况下进入阻塞状态,而这部分内容作为扩充知识点带领大家了解一下。

Block 线程状态图:

在这里插入图片描述

4. Waiting(无限等待)

Wating状态在APl中介绍为:一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。
那么我们之前遇到过这种状态吗?答案是并没有,但并不妨碍我们进行一个简单深入的了解。我们通过一段代码来学习一下:

package test.demo12;

public class WaitingTest {
    public static Object obj = new Object();

    public static void main(String args[]) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj) {
                    try {
                        System.out.println(Thread.currentThread().getName() +"~~~~获取到锁对象,调用wait方法,进入waiting状态,释放锁对象");
//                        obj.wait(); // 无限等待
                        obj.wait(8000); // 计时等待,8秒时间到,自动醒来
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() +"~~~~从waiting状态醒来,获取到锁对象,继续执行了");
                }
            }

        }, "等待线程").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                // 等待3秒钟
                try {
                    System.out.println(Thread.currentThread().getName() +"----等待5秒钟");
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj) {
                    System.out.println(Thread.currentThread().getName() +"----获取到锁对象,调用notify方法,释放锁对象");
                    obj.notify();
                    }
                }
        }, "唤醒线程").start();
    }
}

结果:

唤醒线程----等待5秒钟
等待线程~~~~获取到锁对象,调用wait方法,进入waiting状态,释放锁对象
等待线程~~~~从waiting状态醒来,获取到锁对象,继续执行了
唤醒线程----获取到锁对象,调用notify方法,释放锁对象

通过上述案例我们会发现,一个调用了某个对象的Object.wait方法的线程会等待另一个线程调用此对象的object.notify0方法或Object.notifyAll()方法。

其实waiting状态并不是一个线程的操作,它体现的是多个线程间的通信,可以理解为多个线程之间的协作关系,多个线程会争取锁,同时相互之间又存在协作关系。就好比在公司里你和你的同事们,你们可能存在晋升时的竞争,但更多时候你们更多是一起合作以完成某些任务。

当多个线程协作时,比如A,B线程,如果A线程在Runnable(可运行)状态中调用了wait()方法那么A线程就进入了Waiting(无限等待)状态,同时失去了同步锁。假如这个时候B线程获取到了同步锁,在运行状态中调用了notify0方法,那么就会将无限等待的A线程唤醒。注意是唤醒,如果获取到锁对象,那么A线程唤醒后就进入Runnable(可运行)状态;如果没有获取锁对象,那么就进入到Blocked(锁阻塞状态)。

Waiting 线程状态图:

在这里插入图片描述

5. 补充知识点

在这里插入图片描述

我们在翻阅API的时候会发现Timed Waiting(计时等待)与Waiting(无限等待)状态联系还是很紧密的,比如Waiting(无限等待)状态中wait方法是空参的,而Timed waiting(计时等待)中wait方法是带参的。

这种带参的方法,其实是一种倒计时操作,相当于我们生活中的小闹钟,我们设定好时间,到时通知,可是如果提前得到(唤醒)通知,那么设定好时间在通知也就显得多此一举了,那么这种设计方案其实是一举两得。

如果没有得到(唤醒)通知,那么线程就处于Timed Waiting状态,直到倒计时完毕自动醒来;如果在倒计时期间得到(唤醒)通知,那么线程从Timed Waiting状态立刻唤醒。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值