在Thread中有一个枚举类State,定义了线程生命周期中的六种不同状态,下面先看看线程这几种状态的流转图,然后再分别介绍线程是如何进入这几种不同的状态。
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
这是Thread对象创建后还没有启动(start)时的状态。
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> {});
System.out.println(t.getState());
}
}
创建好的Thread对象调用start方法后状态就会从NEW转为RUNNABLE。
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> { });
t.start();
System.out.println(t.getState());
}
}
以上输出通常是RUNNABLE,但是不一定,因为在执行t.getState()
之前,由于线程调度,有可能线程t已经执行完成并且进入TERMINATED状态。
线程获取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}
上面先后启动了t1和t2两个线程,t1线程先启动后进入synchronizedSleep方法获取锁,t2线程后启动再去调用时,该锁已经被t1线程占有,所以t2线程进入BLOCKED状态。
当线程需要先暂停然后等待其它线程执行完某些操作之后再继续执行,通常会通过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}
在main方法中启动t线程之后调用t.join(),然后main线程等待t线程执行完成,此时main线程进入WAITING状态。
和WATING状态类似,但是等待的时间有限制,当超过这个时间后,即使没有收到其它线程的通知,该线程也会自动恢复运行。我们可以通过以下几种方式进入TIMED_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(10000);
15 }
16}
这个示例和WAITING中的一样,唯一区别就是在t.join()
时设置了超时参数,所以最后main线程处于TIMED_WAITING状态。
线程运行完毕或者异常结束后的状态就是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}
上面说了调用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}
先启动线程t1,它获取lockObj的锁之后调用lockObj.wait()让出锁并进入WAITING状态,此时线程t2启动,它获取lockObj的锁之后调用lockObj.notify(),此时线程t1就进入了BLOCKED状态,等线程t2执行完synchronized代码块并且释放锁之后,线程t1重新获取到lockObj的锁之后进入RUNNABLE状态,再继续执行。