目录
前言
一.线程状态图
二.线程状态
1.初始状态(NEW)
2.运行状态(RUNNING)
3.等待状态(WAITING)
4.超时等待(TIMED_WAITING)
5.阻塞状态(BLOCKED)
6.终止状态(TERMINATED)
三.线程状态间的转换
四.总结
前言
线程状态及其状态转换是线程生命周期中的关键部分,下面我们就来讲解六种线程状态以及其如何转换。
一.线程状态图
线程状态其实是一种枚举类型Thread.State
class T_state{
public static void main(String[] args) {
for(Thread.State state:Thread.State.values()){
System.out.println(state);
}
}
}
- NEW(初始):创建了一个线程,但还没有通过start方法调用。可以理解为安排了工作,但还没有开始工作。
- RUNNABLE(运行):又分为Ready(就绪状态)和Running(运行中状态)。
当线程对象被创建之后,若有线程(比如main线程)调用了该线程对象的start,那么该线程就会进入可运行线程池进行等待线程调用,获取CPU的使用权,此时就处于Ready(就绪状态),当获取到cpu使用权之后,就转变为Running(运行中状态)。
Ready可以理解为即将开始工作,而Running即正在工作中。
- BLOCKED(阻塞):线程试图获取一个锁对象,但此时锁对象被其他线程所占有,那么线程就进入阻塞等待,当获取到锁,则进入Running状态。
- WAITING(等待):线程处于等待状态,等待其他线程唤醒,可以通过notify或者notifyAll方法来唤醒。
- TIMED_WAITING(超时等待):可以在指定的时间内返回。可以使用带参数的sleep或者wait。
- TERMINATED(终止):线程执行结束。主线程和子线程互不影响,子线程并不会因为主线程结束就结束。
二.线程状态
1.初始状态(NEW)
用new关键字新建一个线程对象,这个线程就处于new状态。
public static void main(String[] args) {
// 创建一个线程,该线程在执行时会暂停1000毫秒
Thread t=new Thread(()->{
try {
Thread.sleep(1000); // 模拟线程执行的延迟
} catch (InterruptedException e) {
e.printStackTrace(); // 处理线程被中断的异常
}
});
System.out.println("t的线程状态:"+t.getState()); // 输出线程的当前状态
}
t的线程状态:NEW
2.运行状态(RUNNING)
分为RUNNING(运行状态)和Ready(就绪状态).
public static void main(String[] args) {
Thread t=new Thread(()->{
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
});
t.start();
System.out.println("t的线程状态:"+t.getState()); // 输出线程的当前状态
}
t的线程状态:RUNNABLE
3.等待状态(WAITING)
等待状态下,没有时间限制,等被notify或者notifyAll唤醒。
public static void main(String[] args) throws InterruptedException {
// 获取当前线程对象,用于后续检查线程状态
Thread mainT=Thread.currentThread();
// 使用lambda表达式创建一个新的线程,该线程用于展示线程的运行和状态检查
Thread t=new Thread(()->{
try {
// 使新线程睡眠1秒,模拟耗时操作
Thread.sleep(1000);
}catch (InterruptedException e){
// 如果线程在睡眠时被中断,打印异常信息
e.printStackTrace();
}
// 输出线程的当前状态
System.out.println("main线程状态:"+mainT.getState());
});
// 启动新线程,使其开始执行
t.start();
// 主线程等待新线程完成
t.join();
}
main线程状态:WAITING
4.超时等待(TIMED_WAITING)
TIMED_WAITING线程在等待唤醒,但设置了时限,当到达时限,会自动唤醒线程。
public static void main(String[] args) throws InterruptedException {
// 创建一个线程,该线程执行一个lambda表达式,使线程睡眠1秒
Thread t = new Thread(() -> {
try {
Thread.sleep(1000); // 线程睡眠1000毫秒
} catch (InterruptedException e) {
e.printStackTrace(); // 捕获InterruptedException异常并打印堆栈跟踪
}
});
t.start();
//让main线程休眠
Thread.sleep(50);
System.out.println(t.getState());
}
TIMED_WAITING
5.阻塞状态(BLOCKED)
当进入到synchronized关键字修饰的方法或者代码块时,线程处于阻塞状态。
示例:两个线程争同一个锁对象,此时会引起锁竞争,没有获取到锁的线程,会进入阻塞状态,等待拿到锁的线程将锁释放。
public static void main(String[] args) {
final Object o=new Object();
Thread t=new Thread(()->{
synchronized (o) {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
Thread tt=new Thread(()->{
synchronized (o) {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
t.start();
tt.start();
System.out.println("t的线程状态:"+t.getState()); // 输出线程的当前状态
System.out.println("tt的线程状态:"+tt.getState()); // 输出线程的当前状态
}
t的线程状态:RUNNABLE
tt的线程状态:BLOCKED
6.终止状态(TERMINATED)
当线程执行结束,就处于终止状态。
需要注意:主线程和子线程互不影响,子线程并不会因为主线程结束就结束
/**
* 程序入口
* @param args 命令行参数
* @throws InterruptedException 如果线程被中断
*/
public static void main(String[] args) throws InterruptedException {
// 创建一个线程,该线程将休眠1秒
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
// 启动线程
t.start();
// 等待线程结束
t.join();
// 输出线程的状态
System.out.println(t.getState());
}
TERMINATED
三.线程状态间的转换
1.创建一个线程处于new,让其转换为running状态。可以看到线程t的状态为
NEW->RUNNING->TERMINATED
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.out.println("t线程的状态"+t.getState());
t.start();
System.out.println("t线程的状态"+t.getState());
t.join();
System.out.println("t线程的状态"+t.getState());
}
t线程的状态NEW
t线程的状态RUNNABLE
t线程的状态TERMINATED
2.将处于running的线程转换为wait 。(调用wait或者join都可)
这里以join为例。
public static void main(String[] args) throws InterruptedException {
Thread main = Thread.currentThread();
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("main线程状态:"+main.getState());
});
t.start();
System.out.println("main线程状态:"+main.getState());
t.join();
}
main线程状态:RUNNABLE
main线程状态:WAITING
wait方法:一般在synchronized修饰的代码块或者方法中使用。
调用wait会发生两件事:
(1)线程解锁“锁对象” (2)被解锁的线程会进入waiting状态。
在使用wait方法时,需要先对线程加锁,否则会报错。因为wait会先解锁再进入等待状态。
示例:
public static void main(String[] args) throws InterruptedException {
final Object locker = new Object();
Thread t1=new Thread(()->{
synchronized (locker){
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"t1");
t1.start();
System.out.println("t线程的状态"+t1.getState());
Thread.sleep(50);
System.out.println("t线程的状态"+t1.getState());
}
t线程的状态RUNNABLE
t线程的状态WAITING
但如果我们使用wait带参数的方法,那么状态还会是WAITING吗?
public static void main(String[] args) throws InterruptedException {
final Object locker = new Object();
Thread t1=new Thread(()->{
synchronized (locker){
try {
locker.wait(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"t1");
t1.start();
System.out.println("t线程的状态"+t1.getState());
Thread.sleep(50);
System.out.println("t线程的状态"+t1.getState());
}
t线程的状态RUNNABLE
t线程的状态TIMED_WAITING
可以看到,当我们使用带参数的wait方法时,此时t的状态转变为TIMED_WAITING。
3.若多个线程尝试获取同一个锁,那么就会引起对锁的竞争,即"锁竞争”。没用获取到线程的锁将会进入阻塞状态(BLOCKED),等线程获取到锁,才能执行。
示例:
/**
* 主函数,演示线程间的状态变化
* @param args 命令行参数
* @throws InterruptedException 当线程被中断时抛出此异常
*/
public static void main(String[] args) throws InterruptedException {
// 创建一个对象作为锁
final Object locker = new Object();
// 创建线程t1,使用锁对象进行同步操作
Thread t1=new Thread(()->{
// 使用锁进行同步控制,形成死循环
synchronized (locker){
while(true){
;
}
}
},"t1");
// 启动线程t1
t1.start();
// 输出线程t1的当前状态
System.out.println("t1线程的状态"+t1.getState());
// 创建线程t2,也使用锁对象进行同步操作
Thread t2=new Thread(()->{
// 使用锁进行同步控制,但没有进行循环,所以很快释放锁
synchronized (locker){
;
}
},"t2");
// 启动线程t2
t2.start();
// 输出线程t2的当前状态
System.out.println("t2线程的状态"+t2.getState());
// 主线程等待50毫秒,以观察线程t2的状态变化
Thread.sleep(50);
// 再次输出线程t2的当前状态,以观察其变化
System.out.println("t2线程的状态"+t2.getState());
}
t1线程的状态RUNNABLE
t2线程的状态RUNNABLE
t2线程的状态BLOCKED
我们可以打开jconsole进行查看。
四.总结
当我们创建一个线程对象时,此时线程就处于NEW状态。
调用start方法,会让线程进行运行状态,其中又分为RUNNING(运行中状态)和Ready(就绪状态)。
若想要让RUNNABLE中的线程进入WAITING(等待状态),可以使用wait或者join方法。想唤醒WAITING中的线程,需要使用notify或者nofityAll方法来唤醒。
可以使用带参数的join、sleep和wait方法,让RUNNABLE中的线程进入限时等待(TIMED_WAITING),当达到时时限,会自动唤醒线程。
RUNNABLE中的线程若想出发BLOCKED状态,需要触发锁竞争。
当线程中线程体的内容都执行完之后,就会进入TERMINATED(终止状态)
线程状态的讲解就先到这里了~
若有不足,欢迎指正~