Java进阶 线程状态

1、线程状态

1.线程的六种状态

  • 新建 (NEW) 还没有启动的线程处于此状态。java

  • 可运行 (RUNNABLE) 在Java虚拟机中执行的线程处于此状态。编程

    一旦调用了start方法,线程就处于可运行状态。可运行状态的线程可能正在运行,也可能尚未运行而正在等待 CPU 时间片。(Java规范中并无分为可运行状态和正在运行状态这两个状态,而是把它们合为一个状态。因此,能够把一个正在运行中的线程仍然称其处于可运行状态。)markdown

  • 阻塞 (BLOCKED) 被阻塞等待监视器锁定的线程处于此状态;处于阻塞状态的线程不会占用CPU资源。并发

    如下状况会让线程进入阻塞状态:ide

    ①等待获取锁spa

    等待获取一个锁,而该锁被其它线程持有,则该线程进入阻塞状态。当其它线程释放了该锁,而且线程调度器容许该线程持有该锁时,该线程退出阻塞状态。线程

    ②IO阻塞code

    线程发起了一个阻塞式IO后也会进入阻塞状态。最典型的场景,如等待用户输入内容而后继续执行。orm

  • 无限期等待 (WAITING) 正在等待另外一个线程执行特定动做的线程处于此状态。对象

    处于这种状态的线程不会被分配 CPU 时间片,须要等待其它线程显式地唤醒。

    如下方法会让线程进入无限期的等待状态

    • Object.wait() 方法 结束:Object.notify() / Object.notifyAll()
    • Thread.join() 方法 结束:被调用的线程执行完毕
    • LockSupport.park() 方法 结束:LockSupport.unpark(currentThread)
  • 限时等待 (TIMED_WAITING) 正在等待另外一个线程执行动做达到指定等待时间的线程处于此状态。

    处于这种状态的线程也不会被分配CPU 时间片,在必定时间以后会被系统自动唤醒。

    如下方法会让线程进入限期等待状态:

    • Thread.sleep(time) 方法 结束:sleep时间结束

    • Object.wait(time) 方法 结束:wait时间结束,或者Object.notify() / notifyAll()

    • LockSupport.parkNanos(time)/parkUntil(time) 方法 结束:park时间结束,或者LockSupport.unpark(当前线程)

  • 死亡 (TERMINATED) 已退出的线程处于此状态。

注意:在有些书籍(如《Java并发编程实战》中5.4章节)或笼统的称呼中,就将阻塞、等待和超时等待这三种状态统称为阻塞状态。

2.线程状态图示

2、等待、阻塞、中断、睡眠、挂起

描 述 释放锁 cpu
等待 线程因等待某个条件而进入等待状态 会释放锁 不会被分配CPU时间片
阻塞 线程因竞争锁失败而进入阻塞状态 未获取到锁
睡眠 让出CPU的使用权让其它线程执行 不会释放锁 让出CPU的使用权
挂起
yield 让出CPU的使用权,进入就绪状态(runnable) 让出CPU的使用权
中断 并不是终止线程,而是给该线程发送一个中断通知,让其自行决定在合适的时间对中断通知作出响应(响应能够是终止线程,或者不作出响应)。
join 等待调用该方法的线程执行完毕后,线程再往下继续执行

1.阻塞状态和等待状态的区别

阻塞状态:等待获取锁或者IO阻塞。

等待状态:因条件的不容许而暂停运行,会释放锁。

2.wait和sleep的区别

①sleep来自Thread类,而wait来自Object类。

②sleep不会释放锁,而wait会释放了锁。

③sleep必须捕获异常,而wait/notify/notifyAll不须要捕获异常。

④wait/notify/notifyAll必须在同步方法或同步代码块中调用,而sleep没有这方面的限制。 (缘由见本文末)

3.interrupt

线程中断并不是线程终止,而是要给该线程发一个中断信号让它本身决定如何处理。

其实是设置了线程的中断标志位,在线程阻塞的地方(如调用sleep、wait、join等地方)抛出一个异常InterruptedException,而且中断状态也将被清除,从新复位为false,这样线程就得以退出阻塞的状态。

实例

/** * 等待唤醒案例:线程中的通讯 * 建立一个顾客线程(消费者):告知老板要的包子数量,调用 Wait()方法,放弃cpu执行,进入无限等待状态 * 建立一个老板线程(生产者):花了5秒作包子,作好以后调用 Notify()方法,唤醒顾客 * <p> * 注意: * 顾客和老板线程必须使用同步代码块包裹,保证等待和唤醒只能有一个在执行; * 同步使用的锁对象必须保证惟一; * 只有锁对象才能调用 wait() 和 Notify()方法 * * @Author: iwen大大怪 * @DateTime: 2020/6/15 20:48 */
public class WaitAndNotify {
    public static void main(String[] args) {
        // 建立一个锁对象
        Object obj = new Object();
        // 建立一个顾客线程
        new Thread(){
            @Override
            public void run() {
                // 保证等待和唤醒只能有一个在执行
                synchronized (obj){
                    System.out.println("告知老板要的包子数量");
                    // 调用 Wait()方法,放弃cpu执行,进入无限等待状态
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 顾客被唤醒
                    System.out.println("包子已经作好了,能够吃了");
                }
            }
        }.start();

        // 建立一个顾客线程
        new Thread(){
            @Override
            public void run() {
                // 花了5秒作包子
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 保证等待和唤醒只能有一个在执行
                synchronized (obj){
                    System.out.println("老板花了5秒");
                    // 调用 Notify()方法,唤醒顾客
                    obj.notify();
                }
            }
        }.start();
    }
}

复制代码
相关文章
相关标签/搜索