实用 AI

可在线运行 AI 集合,涵盖 AI 文案生成、写作辅助、AI 绘图与照片修复、AI 配音、字幕生成、语音转录以及 AI 视频创作和数字人等多种 AI 服务

查看详情

Thread的生命周期和状态

深入学习Java基础知识
2021-05-17 14:29 · 阅读时长9分钟

在Thread中有一个枚举类State,定义了线程生命周期中的六种不同状态,下面先看看线程这几种状态的流转图,然后再分别介绍线程是如何进入这几种不同的状态。

加载中...
public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}
NEW

这是Thread对象创建后还没有启动(start)时的状态。

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {});
        System.out.println(t.getState());
    }
}
注意: 这个Java运行环境不支持自定义包名,并且public class name必须是Main
RUNNABLE

创建好的Thread对象调用start方法后状态就会从NEW转为RUNNABLE。

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(() -> { });
        t.start();
        System.out.println(t.getState());
    }
}
注意: 这个Java运行环境不支持自定义包名,并且public class name必须是Main

以上输出通常是RUNNABLE,但是不一定,因为在执行t.getState()之前,由于线程调度,有可能线程t已经执行完成并且进入TERMINATED状态。

BLOCKED

线程获取monitor lock也就是调用synchronized方法或者进入synchronized代码块时,如果该锁已经被其它线程所持有,那么该线程就会进入BLOCKED状态。另外只有获取monitor lock这种锁时才会进入BLOCKED状态,而获取java.util.concurrent.locks.Lock这种锁是进入WAITING状态,因为Lock内部是使用LockSupport.park让线程进入等待状态。

1public class Main {
2
3    public static synchronized void synchronizedSleep() {
4        try {
5            Thread.sleep(3000);
6        } catch (InterruptedException e) {
7            e.printStackTrace();
8        }
9    }
10
11    public static void main(String[] args) {
12        Thread t1 = new Thread(Main::synchronizedSleep);
13        t1.start();
14        try {
15            Thread.sleep(1000);
16        } catch (InterruptedException e) {
17            e.printStackTrace();
18        }
19        Thread t2 = new Thread(Main::synchronizedSleep);
20        t2.start();
21        try {
22            Thread.sleep(1000);
23        } catch (InterruptedException e) {
24            e.printStackTrace();
25        }
26        System.out.println(t2.getState());
27    }
28}
注意: 这个Java运行环境不支持自定义包名,并且public class name必须是Main

上面先后启动了t1和t2两个线程,t1线程先启动后进入synchronizedSleep方法获取锁,t2线程后启动再去调用时,该锁已经被t1线程占有,所以t2线程进入BLOCKED状态。

WAITING

当线程需要先暂停然后等待其它线程执行完某些操作之后再继续执行,通常会通过object.wait()、thread.join() 或者LockSupport.park()方法进入WAITING状态。

1public class Main {
2
3    public static void main(String[] args) throws InterruptedException {
4        Thread main = Thread.currentThread();
5        Thread t = new Thread(() -> {
6            try {
7                Thread.sleep(1000);
8            } catch (InterruptedException e) {
9                e.printStackTrace();
10            }
11            System.out.println(main.getState());
12        });
13        t.start();
14        t.join();
15    }
16}
注意: 这个Java运行环境不支持自定义包名,并且public class name必须是Main

在main方法中启动t线程之后调用t.join(),然后main线程等待t线程执行完成,此时main线程进入WAITING状态。

TIMED_WAITING

和WATING状态类似,但是等待的时间有限制,当超过这个时间后,即使没有收到其它线程的通知,该线程也会自动恢复运行。我们可以通过以下几种方式进入TIMED_WAITING状态。

  • thread.sleep(long millis)
  • wait(int timeout) or wait(int timeout, int nanos)
  • thread.join(long millis)
  • LockSupport.parkNanos
  • LockSupport.parkUntil
1public class Main {
2
3    public static void main(String[] args) throws InterruptedException {
4        Thread main = Thread.currentThread();
5        Thread t = new Thread(() -> {
6            try {
7                Thread.sleep(1000);
8            } catch (InterruptedException e) {
9                e.printStackTrace();
10            }
11            System.out.println(main.getState());
12        });
13        t.start();
14        t.join(10000);
15    }
16}
注意: 这个Java运行环境不支持自定义包名,并且public class name必须是Main

这个示例和WAITING中的一样,唯一区别就是在t.join()时设置了超时参数,所以最后main线程处于TIMED_WAITING状态。

TERMINATED

线程运行完毕或者异常结束后的状态就是TERMINATED。

1public class Main {
2    public static void main(String[] args) {
3        Thread t = new Thread(() -> { });
4        t.start();
5        try {
6            Thread.sleep(1000);
7        } catch (InterruptedException e) {
8            e.printStackTrace();
9        }
10        System.out.println(t.getState());
11    }
12}
注意: 这个Java运行环境不支持自定义包名,并且public class name必须是Main
WAITING/TIMED_WAITING和BLOCKED的关联

上面说了调用object.wait()会进入WAITING状态,但是有一种特殊情况,会让处于WAITING状态的线程进入BLOCKED状态,下面先看一个示例。

1public class Main {
2
3    private static final Object lockObj = new Object();
4
5    public static void main(String[] args) {
6        Thread t1 = new Thread(() -> {
7            synchronized (lockObj) {
8                try {
9                    lockObj.wait();
10                } catch (InterruptedException e) {
11                    e.printStackTrace();
12                }
13            }
14        });
15        t1.start();
16        try {
17            Thread.sleep(1000);
18        } catch (InterruptedException e) {
19            e.printStackTrace();
20        }
21        Thread t2 = new Thread(() -> {
22            synchronized (lockObj) {
23                System.out.println("t1: " + t1.getState());
24                lockObj.notify();
25                System.out.println("t1: " + t1.getState());
26            }
27        });
28        t2.start();
29    }
30}
注意: 这个Java运行环境不支持自定义包名,并且public class name必须是Main

先启动线程t1,它获取lockObj的锁之后调用lockObj.wait()让出锁并进入WAITING状态,此时线程t2启动,它获取lockObj的锁之后调用lockObj.notify(),此时线程t1就进入了BLOCKED状态,等线程t2执行完synchronized代码块并且释放锁之后,线程t1重新获取到lockObj的锁之后进入RUNNABLE状态,再继续执行。

thread线程生命周期状态java