Java中线程的生命周期

1 介绍

本篇文章我们讨论下Java中的一个非常核心的概念:线程的生命周期。在Java编程语言中,多线程编程非常重要。线程从创建到销毁是有生命周期的,在线程的生命周期中,线程会经历多种状态(state)。

2 线程状态说明以及状态流转图

java.lang.Thread类中有一个静态的枚举类:State,用来表示线程各种可能的状态:

public enum State {
        /**
         * NEW状态表示线程新创建,仍不能开始运行。
         */
        NEW,
​
        /**
         * RUNNABLE表示线程可运行(runnable or ready to run),有可能正在运行,或者正在资源ready,比如CPU调度。
         */
        RUNNABLE,
​
        /**
         * BLOCKED表示线程正在等待监视器锁(monitor lock),获取到监视器锁后可以进入同步代码块或者同步方法(synchronized block/method),当然可能是首次等待,也可能是调用Object.wait()等待之后再被唤醒(Object.notify()或者Object.notifyAll())后,因为唤醒后一般不能直接进入Runnable状态,需要重写争抢监视器锁,这时仍然是BLOCKED状态。
         */
        BLOCKED,
​
        /**
         * WAITING状态表示当前线程正在等待其他线程的一些动作(无超时时间的等待),包括如下三个场景:
         * Object.wait()调用之后等待其他线程调用Object.notify()或者Object.notifyAll()
         * Thread.join()调用之后等待指定的线程终止(进入TERMINATED状态)
         * LockSupport.park()调用之后等待等待其他线程调用LockSupport.unpark(thread)
         */
        WAITING,
​
        /**
         * TIMED_WAITING状态表示当前线程在限定时间内等待其他线程的一些动作,包括如下几个场景:
         * Thread.sleep
         * Object.wait指定等待时间
         * Thread.join指定等待时间
         * LockSupport.parkNanos
         * LockSupport.parkUntil
         */
        TIMED_WAITING,
​
        /**
         * TERMINATED表示终止状态,线程已经执行完成。
         */
        TERMINATED;
    }
复制代码

线程的可能状态流转图(状态机)如下所示:

Java中线程的生命周期_第1张图片

注意上面的Blocked状态,线程调用Object.wait()等待之后会进入WAITINGTIMED_WAITING状态,而再被唤醒(Object.notify()或者Object.notifyAll())后,由于唤醒后一般不能直接进入Runnable状态,需要重写争抢监视器锁,这时仍然是先进入BLOCKED状态。

3 线程各状态实战举例

3.1 NEW

NEW状态表示线程新创建,仍不能开始运行。当调用了线程的start()方法后线程才开始运行。下面的代码表示新创建的线程处于NEW状态:

Runnable runnable = new Runnable() {
  @Override
  public void run() {
    System.out.println("线程任务运行中...");
  }
};
Thread thread = new Thread(runnable);
System.out.println(thread.getState());
复制代码

运行结果如下:

3.2 Runnable

RUNNABLE表示线程可运行(runnable or ready to run),有可能正在运行,或者正在资源ready,比如CPU调度。在多线程环境下,JVM中的线程调度器给每个线程分配一定的时间片去执行,执行完分配的之后之后,换为其他线程执行(分时复用机制)。我们继续基于前面的代码,把线程启动起来,然后看下线程的状态:

Runnable runnable = new Runnable() {
  @Override
  public void run() {
    System.out.println("线程任务运行中...");
  }
};
Thread thread = new Thread(runnable);
thread.start();
System.out.println(thread.getState());
复制代码

运行结果如下:

有一点需要注意:由于线程启动之后可能非常快就直接执行完,因此可能输出TERMINATED状态。

3.3 Blocked

当一个线程等待获取监视器锁(monitor lock)的时候,会处于BLOCKED状态,之所以要等待获取监视器锁,是因为当前监视器锁正在被其他线程占有。下面我们复现该线程状态:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new DemoBlockedRunnable());
        Thread t2 = new Thread(new DemoBlockedRunnable());
        t1.start();
        t2.start();
        Thread.sleep(1000);
        System.out.println(t2.getState());
        System.exit(0);
    }
}
​
class DemoBlockedRunnable implements Runnable {
    @Override
    public void run() {
        commonResource();
    }
​
    public static synchronized void commonResource() {
        while(true) {
            // Infinite loop to mimic heavy processing
            // 't1' won't leave this method
            // when 't2' try to enter this
        }
    }
}
复制代码

输出如下:

上述代码中,由于t1先抢占了类DemoBlockedRunnable上的监视器锁,并且t1进入了无限循环,导致t2一直在等待获取监视器锁,因此打印t2的线程状态为BLOCKED

3.4 Waiting

WAITING状态表示当前线程正在等待其他线程的一些动作(无超时时间的等待),包括如下三个场景:

  1. object.wait()调用之后等待其他线程调用Object.notify()或者Object.notifyAll();
  2. thread.join()调用之后等待指定的线程终止(进入TERMINATED状态);
  3. LockSupport.park()调用之后等待等待其他线程调用LockSupport.unpark(thread);

下面我们复现该线程状态:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable1());
        Thread t2 = new Thread(new Runnable2(t1));
        t1.start();
        t2.start();
        Thread.sleep(1000);
        System.out.println(t2.getState());
    }
}
​
class Runnable1 implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable1 begin run...");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Runnable1 end run...");
    }
}
​
class Runnable2 implements Runnable {
    private Thread runnable1;
    public Runnable2(Thread runnable1) {
        this.runnable1 = runnable1;
    }
    @Override
    public void run() {
        System.out.println("Runnable2 begin run...");
        try {
            runnable1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Runnable2 end run...");
    }
}
复制代码

输出如下:

Java中线程的生命周期_第2张图片

这里由于线程t2调用了thread.join(),因此陷入WAITING状态。

3.5 Timed Waiting

TIMED_WAITING状态表示当前线程在限定时间内等待其他线程的一些动作,包括如下几个场景:

  1. Thread.sleep(long mills)
  2. object.wait(int timeout)
  3. thread.join(long mills)
  4. LockSupport.parkNanos
  5. LockSupport.parkUntil
public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        Thread.sleep(1000);
        System.out.println(t1.getState());
    }
}
复制代码

输出如下:

这里由于线程t1调用了Thread.sleep(2000),因此在等待期内的状态为TIMED_WAITING

3.6 Terminated

TERMINATED是线程终止状态,线程完成执行或者异常终止后都会处于TERMINATED状态。

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1 running begin..");
                System.out.println("t1 running end..");
            }
        });
        t1.start();
        Thread.sleep(1000);
        System.out.println(t1.getState());
    }
}
复制代码

输出如下:

4 总结

线程是Java多线程编程的基础,本文讨论了Java中线程的生命周期,以及生命周期中可能出现的6种状态,每种状态都举例进行了说明。

你可能感兴趣的:(java,java,jvm,开发语言)