线程的生命周期

通用线程状态

  1. 初始状态

    线程已被创建,但是还不被允许分配CPU执行

    注意,这个被创建其实是属于编程语言层面的(比如Java语言中的new Thread()),实际在操作系统里,真正的线程还没被创建

  2. 可运行状态

    线程可以分配CPU执行这时,操作系统中线程已经被创建成功了

  3. 运行状态

    操作系统会为处在可运行状态的线程分配CPU时间片,处在可运行状态的线程,就会变为运行状态

  4. 休眠状态

    如果处在运行状态的线程调用某个阻塞的API等待某个事件条件可用,那么线程就会转换到休眠状态。

    注意:此时线程会释放CPU使用权休眠的线程永远没有机会获得CPU使用权,只有当等待事件出现后,线程会从休眠状态转换到可运行状态。

  5. 终止状态

    线程执行完或者出现异常 (被interrupt不算)就会进入终止状态,正式走到生命的尽头,没有起死回生的机会。

Java线程状态

Java 语言中

  • 将通用线程状态的可运行状态运行状态合并为 Runnable

  • 休眠状态细分为三种 (BLOCKEDWAITINGTIMED_WAITING),这三种状态在操作系统的眼中都是休眠状态,同样不会获得CPU使用权

Thread的源码中,定义了一个枚举类State

public enum State {
    /**
     * Thread state for a thread which has not yet started.
     */
    NEW,

    /**
     * Thread state for a runnable thread.  
     * A thread in the runnable state is executing in the Java virtual machine 
     * but it may be waiting for other resources from the operating system such as processor.
     */
    RUNNABLE,

    /**
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED,

    /**
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the following methods:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * A thread in the waiting state is waiting for another thread to perform a particular action.
     * For example, 1) a thread that has called <tt>Object.wait()</tt> on an object is waiting for another thread to call
     * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on that object. 
     * 2) A thread that has called <tt>Thread.join()</tt> is waiting for a specified thread to terminate.
     */
    WAITING,

    /**
     * Thread state for a waiting thread with a specified waiting time.
     * A thread is in the timed waiting state due to calling one of the following methods with a specified positive waiting time:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING,

    /**
     * Thread state for a terminated thread. The thread has completed execution.
     */
    TERMINATED;
}

NEW状态

用关键字new创建一个Thread对象(在没有start之前,该线程根本不存在,这只是一个Java对象)时,此时它并不处于执行状态,因为没有调用start方法启动该线程,那么线程的状态为NEW状态:

Thread thread = new Thread(() -> {});
System.out.println(thread.getState());

Runnable可运行、运行状态

Java将通用线程状态的可运行状态运行状态合并为Runnable

线程对象进入RUNNABLE状态必须调用start方法,此时才是真正地在JVM进程中创建了一个线程,线程一经启动并不是就立即开始执行,其运行与否需要听令于CPU的调度。处于ready to run状态,只是具备了执行的资格

一旦CPU通过轮询或者其他方式,从任务可执行队列中选中了线程,那么此时它才能真正地执行自己的逻辑代码。如果由于CPU的调度器轮询,使该线程放弃执行,或者线程主动调用yield方法,会放弃CPU执行权,由running进入ready to run状态(此时线程状态显示仍为Runnable)!

调用了start()方法之后,线程就处在RUNNABLE状态了:

Thread thread = new Thread(() -> {});
thread.start();
//Thread.sleep(1000);
System.out.println(thread.getState());

在该状态进入休眠状态的情况:

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

    • 调用不带时间参数的等待API或者join(),就会从RUNNABLE状态进入到WAITING状态。当被唤醒就会从WAITING进入RUNNABLE状态;
    • 调用带时间参数的等待API,自然就从RUNNABLE状态进入TIMED-WAITING状态。当被唤醒或超时时间到就会从TIMED_WAITING进入RUNNABLE状态。
  • 进行某个阻塞的IO操作

  • 获取某个锁资源,从而加入到该锁的阻塞队列:

    • 当且仅有(just only)一种情况会从RUNNABLE状态进入到BLOCKED状态,就是线程在等待synchronized内置隐式锁
    • 如果等待的线程获取到了synchronized内置隐式锁,也就会从BLOCKED状态变为RUNNABLE状态了。

休眠状态

BLOCKED状态

等待synchronized内置锁,就会处在BLOCKED状态:

public class ThreadStateTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new DemoThread());
        Thread thread2 = new Thread(new DemoThread());

        thread1.start();
        thread2.start();
        
        // 防止两个线程都没有获得锁
        Thread.sleep(100);

        System.out.println("thread1.getState(): " + thread1.getState());
        System.out.println("thread2.getState(): " + thread2.getState());

        System.exit(0);
    }
}

class DemoThread implements Runnable {
    @Override
    public void run() {
        commonResource();
    }

    public static synchronized void commonResource() {
        while (true) {}
    }
}

输出结果:

thread1.getState(): RUNNABLE
thread2.getState(): BLOCKED

WAITING状态

调用线程的join()等方法,从RUNNABLE变为WAITING状态:

public static void main(String[] args) throws InterruptedException {
    Thread main = Thread.currentThread();

    Thread thread2 = new Thread(() -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
        System.out.println(main.getState());  // 此时main线程是WAITING状态
    });
    thread2.start();
    thread2.join();
}

TIMED-WAITING状态

调用了sleep(long)等方法,线程从RUNNABLE变为TIMED-WAITING状态:

public static void main(String[] args) throws InterruptedException {
    Thread thread3 = new Thread(() -> {
        try {
            Thread.sleep(3000); // 时间要比主线程睡眠时间长
        } catch (InterruptedException e) {
    		  // 为什么要调用interrupt方法?
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    });
    thread3.start();

    Thread.sleep(1000);
    System.out.println(thread3.getState());
}

在该状态中,线程的状态可以发生如下的状态转换:

  • 直接进入TERMINATED状态
  • 进入RUNNABLE状态:
    • 线程完成了指定时间的休眠
    • wait中的线程被其他线程notify/notifyAll唤醒;
    • 线程获取到了某个锁资源;
    • 线程阻塞的操作结束;
    • 线程在阻塞过程中被打断,比如其他线程调用了interrupt方法。

TERMINATED状态

线程进入TERMINATED状态,意味着该线程的整个生命周期都结束了。

Thread thread = new Thread(() -> {});
thread.start();
Thread.sleep(1000);  // 让线程thread先结束
System.out.println(thread.getState());

轻量级阻塞与重量级阻塞

  • 能够被中断的阻塞称为轻量级阻塞,对应的线程状态是WAITING或者TIMED_WAITING
  • 而像synchronized这种不能被中断的阻塞称为重量级阻塞,对应的状态是BLOCKED

初始线程处于NEW状态,调用start()之后开始执行,进入RUNNING或者RUNNABLE状态。如果没有调用任何的阻塞函数,线程只会在RUNNINGRUNNABLE之间切换,也就是系统的时间片调度。这两种状态的切换是操作系统完成的,开发者基本没有机会介入,除了可以调用yield()函数,放弃对CPU的占用

一旦调用了图中的任何阻塞函数,线程就会进入WAITING(无限期阻塞)或者TIMED_WAITING状态(传入了一个时间参数,阻塞一个有限的时间)。如果使用了synchronized关键字或者synchronized块,则会进入BLOCKED状态

posted @ 2021-03-13 22:28  chenzufeng  阅读(189)  评论(0编辑  收藏  举报