zoukankan      html  css  js  c++  java
  • JAVA进阶系列

    目标

    1. 线程的生命周期

    2. 线程的状态定义

    3. 线程的状态转移

    内容

    1. 线程的生命周期说明

    上一篇文章中,我们简单的描述了同步与异步的差异以及线程的基本使用。那么今天我们就来了解一下线程的生命周期。

    在调用了 Thread 类对象的 start 方法来启动 Java 线程后,对应的底层操作系统线程不能马上得到 CPU 时间片来执行,需要等待操作系统的调度。所以,为了便于跟踪 Java 线程的执行情况,Thread 类定义了一系列的线程状态来表示当前线程的执行情况,同时整个线程的整个生命周期就是在这些状态直接转换。

    2. 线程的状态定义

    为了便于对 Java 的线程进行管理和对线程的运行情况进行跟踪,Java 定义了 6 个线程状态来表示线程的当前运行情况,这6个状态具体在 Thread 类的内部枚举类 State 中定义:

    public enum State {
        // 新建状态
        NEW,
        // 可运行状态
        RUNNABLE,
        // 阻塞状态
        BLOCKED,
        // 等待状态
        WAITING,
        // 超时等待状态
        TIMED_WAITING,
        // 终止状态
        TERMINATED;
    }
    
    

    2.1. 新建状态 NEW

    当我们用关键字 new 创建一个 Thread 对象时,因为没有调用 start 方法启动该线程,此时的 Thread 对象就是一个普通的 Java 对象,并不会执行任何操作。

    @Slf4j
    public class ThreadStateDemo {
        public static void main(String[] args) {
            Thread t1 = new Thread(() -> {
                log.debug("running...");
            }, "t1");
    
            log.debug("t1 state {}", t1.getState());    // t1 state NEW
        }
    }
    
    

    2.2. 可运行状态 RUNNABLE

    线程通过调用 start 方法启动,使该线程对象进入 RUNNABLE 状态,此时才真正地在 JVM 进程中创建了一个线程。

    但是线程启动之后可以立即得到执行吗?答案是否定的。线程的运行与否和进程一样都要听命于 CPU 的调度。如果操作系统的 CPU 此时是空闲的,则该线程对象对应的线程可以立即获取到 CPU 时间片并执行(此时的线程状态为运行中running)。反之,如果此时 CPU 繁忙,不能马上分配 CPU 时间片资源来执行该线程,则该线程需要等待操作系统之后的调度来获得 CPU 时间片并执行(此时的线程状态为就绪ready)。

    @Slf4j
    public class ThreadStateDemo {
        public static void main(String[] args) {
            Thread t2 = new Thread(() -> {
                while(true){
                    // runnable
                }
            }, "t2");
            t2.start();
    
            log.debug("t2 state {}", t2.getState());    // t2 state RUNNABLE
        }
    }
    
    

    #### 2.3. 阻塞状态 BLOCKED

    当线程处于阻塞状态 BLOCKED 时,线程不能继续往下执行。这种情况主要出现在如下几种情况中:

    • 调用了 sleep 或者 wait 方法而加入了 waitSet 中;

    • 进行某个阻塞的 IO 操作;

    • 获取某个锁资源失败从而加入到该锁的阻塞队列中;

    @Slf4j
    public class ThreadStateDemo {
        public static void main(String[] args) {
            Thread t5 = new Thread(() -> {
                synchronized (ThreadStateDemo.class) {
                    try {
                        // time_waiting
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t5");
            t5.start();
    
            Thread t3 = new Thread(() -> {
                // t5 线程先占用锁,所以该线程会进入 blocked 状态
                synchronized (ThreadStateDemo.class) {
                    try {
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t3");
            t3.start();
    
            log.debug("t3 state {}", t3.getState());    // t3 state BLOCKED
        }
    }
    
    

    #### 2.4. 等待状态 WAITING

    线程处于等待状态 WAITING 主要出现在与其他线程进行协作的场景中,具体为当前线程等待其他线程执行某种操作来通知和唤醒当前线程。当前线程处于等待状态 WAITING 时,除了不能继续往下执行外,还有个特性是如果当前线程持有锁,如在 synchronized 方法内或者 synchronized 同步代码块内部执行时,线程进入了等待状态,则会自动释放锁,这样其他线程可以竞争获取该锁。

    在 Thread 类的方法设计中,主要是基于 Object 类的 wait、notify 和 notifyAll 方法实现的,其中线程调用 wait 方法时,进入等待状态。另外当线程调用 Thread 类的 join 方法或者调用 LockSupport 的静态 park 方法时,线程也会进入等待状态 WAITING。

    @Slf4j
    public class ThreadStateDemo {
        public static void main(String[] args) {
            Thread t2 = new Thread(() -> {
                while(true){
                    // runnable
                }
            }, "t2");
            t2.start();
    
            Thread t4 = new Thread(() -> {
                try {
                    // waiting
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "t4");
            t4.start();
    
            log.debug("t4 state {}", t4.getState());    // t4 state WAITING
        }
    }
    
    

    #### 2.5. 超时等待状态 TIMED_WAITING

    超时等待状态 TIMED_WAITING 与等待状态 WAITING 功能类似,不同之处在于 WAITING 状态的等待不支持指定等待时间,没有超时机制,一旦条件不满足就会无限等待下去。而处于超时等待状态 TIMED_WAITING 的线程,指定了最长等待时间,如果超过这个时间条件还未满足,则当前线程会自动唤醒并继续往下执行。

    @Slf4j
    public class ThreadStateDemo {
        public static void main(String[] args) {
            Thread t5 = new Thread(() -> {
                synchronized (ThreadStateDemo.class) {
                    try {
                        // time_waiting
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "t5");
            t5.start();
    
            log.debug("t5 state {}", t5.getState());    // t5 state TIMED_WAITING
        }
    }
    
    

    #### 2.6. 终止状态 TERMINATED

    TERMINATED 是一个线程的最终状态。当 Thread 线程对象对应的现场执行完毕时,线程进入终止状态 TERMINATED,在该状态中线程将不会切换到其他任何状态。这意味着该线程整个生命周期都结束了,对应的 Thread 类对象也会被回收销毁。下列这些情况都将会使线程进入 TERMINATED 状态:

    • 线程正常运行结束;

    • 线程意外出错结束;

    • JVM Crash,导致所有线程结束;

    @Slf4j
    public class ThreadStateDemo {
        public static void main(String[] args) {
            Thread t6 = new Thread(() -> {
                log.debug("running...");
            }, "t6");
            t6.start();
    
            try {
                // 这里主线程休眠一下,让
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            log.debug("t6 state {}", t6.getState());    // t6 state TERMINATED
        }
    }
    
    

    ### 3. 线程的状态转换

    线程状态转换

    图片来源:B站 - 黑马程序员

    上图中箭头表示可由该状态向对应状态转换。其中,RUNNABLE 状态涵盖了操作系统层面的 【可运行状态】【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在Java里面无法区分,仍然认为是可运行)

    总结

    本篇我们学习了线程的生命周期,在使用多线程的过程中,线程的生命周期将会贯穿始终,只有清晰地掌握生命周期各个阶段的切换,才能更好地理解线程的阻塞以及唤醒机制,同时也为掌握同步锁等概念打下一个良好的基础。

    今天的文章到这里就结束了,小伙伴们有什么建议或者意见请联系我改进哦,微信公众号:【该昵称无法识别】,你们的支持是我最大的动力!!!

    微信公众号

  • 相关阅读:
    2019 SDN上机第1次作业
    关键路径法(Critical Path Method, CPM)
    iOS 一个项目添加多个TARGET
    为图形处理器提供数据
    OpenGL全流程详细解读
    小技巧之padding-bottom实现等比例图片缩放
    Mac 显示隐藏文件
    mac 下修改 jenkins 端口以及Jenkins的启动、关闭与更新
    Mac上Charles抓包iOS的https请求
    python自动循环重启android系统
  • 原文地址:https://www.cnblogs.com/unrecognized/p/14273229.html
Copyright © 2011-2022 走看看