目标
-
线程的生命周期
-
线程的状态定义
-
线程的状态转移
内容
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里面无法区分,仍然认为是可运行)
总结
本篇我们学习了线程的生命周期,在使用多线程的过程中,线程的生命周期将会贯穿始终,只有清晰地掌握生命周期各个阶段的切换,才能更好地理解线程的阻塞以及唤醒机制,同时也为掌握同步锁等概念打下一个良好的基础。
今天的文章到这里就结束了,小伙伴们有什么建议或者意见请联系我改进哦,微信公众号:【该昵称无法识别】,你们的支持是我最大的动力!!!