在线程的生命周期中,它需要经历初始状态、可运行状态、阻塞状态、等待状态、超时等待状态和终止状态。线程的状态流转如下图所示。
下面的示例代码都继承自类,用来按步骤(模拟时间序列)输出线程的状态。
package org.gettingreal.juc.learning.thread;
/**
* 线程状态打印类
*/
public class ThreadStatePrinter {
// 记录步骤
private static volatile int STEP = 0;
// 打印状态
public static void printState(Thread thread) {
System.out.println("STEP: " + (++STEP) + ", " + thread.getName() + "'s state: " + thread.getState());
}
}
继承 Thread 类,或者类实现 Runnable 接口并将其传递给 Thread 类,使用 new 操作符来创建一个线程,线程此时的状态是初始状态。
package org.gettingreal.juc.learning.thread;
/**
* 线程初始化状态测试
*/
public class ThreadNewStateTest extends ThreadStatePrinter {
public static void main(String[] args) {
Thread t = new Thread();
printState(t);
// 输出
// STEP: 1, Thread-0's state: NEW
}
}
线程的可运行状态较为复杂,因为隐喻了就绪状态和运行状态。
就绪状态,是指线程有被运行的可能,CPU 分配给该线程时间片,该线程就会进入运行状态。
下面是线程进入就绪状态的场景:
通过 start() 方法使得线程进入 RUNNABLE 状态。
package org.gettingreal.juc.learning.thread;
/**
* 线程 RUNNABLE 状态测试
*/
public class ThreadRunnableStateWithStartTest extends ThreadStatePrinter {
public static void main(String[] args) {
Thread t = new Thread();
printState(t);
t.start();
printState(t);
// 输出
// STEP: 1, Thread-0's state: NEW
// STEP: 2, Thread-0's state: RUNNABLE
}
}
线程 sleep() 方法结束,进入 RUNNABLE 状态。
package org.gettingreal.juc.learning.thread;
/**
* 线程 RUNNABLE 状态测试
*/
public class ThreadRunnableStateWithSleepTest extends ThreadStatePrinter {
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
// 触发睡眠
Thread.sleep(3000);
// 睡眠之后打印该线程的状态
printState(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
printState(t);
t.start();
printState(t);
// 主线程睡眠 1 秒中,为了输出睡眠中线程的状态
Thread.sleep(1000);
printState(t);
// 输出
// STEP: 1, Thread-0's state: NEW
// STEP: 2, Thread-0's state: RUNNABLE
// STEP: 3, Thread-0's state: TIMED_WAITING
// STEP: 4, Thread-0's state: RUNNABLE
}
}
可以看出,当线程调用 sleep() 方法后,线程进入 TIMED_WAITING 状态,等到 sleep 结束,线程又进入到 RUNNABLE 状态。
其他线程 join 结束,线程进入 RUNNABLE 状态。
package org.gettingreal.juc.learning.thread;
/**
* 线程 RUNNABLE 状态测试
*/
public class ThreadRunnableStateWithSleepTest extends ThreadStatePrinter {
private static Thread parentThread;
private static Thread childThread;
public static void main(String[] args) throws Exception {
childThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// 触发睡眠
Thread.sleep(3000);
// 睡眠之后打印父线程的状态
printState(parentThread);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
childThread.setName("childThread");
parentThread = new Thread(new Runnable() {
@Override
public void run() {
try {
childThread.start();
childThread.join();
// join 结束后打印父线程状态
printState(parentThread);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
parentThread.setName("parentThread");
printState(parentThread);
parentThread.start();
printState(parentThread);
// 输出
// STEP: 1, parentThread's state: NEW
// STEP: 2, parentThread's state: RUNNABLE
// STEP: 3, parentThread's state: WAITING
// STEP: 4, parentThread's state: RUNNABLE
}
}
可以看出,parentThread 线程在 WAITING 状态之后又恢复到 RUNNABLE 状态。
时间片用完或者调用 yield() 方法。
package org.gettingreal.juc.learning.thread;
/**
* 线程 RUNNABLE 状态测试
*/
public class ThreadRunnableStateWithYieldTest extends ThreadStatePrinter {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
printState(Thread.currentThread());
// 正在运行中的线程,时间片用完或者调用 yield 方法,都会让线程进入 RUNNABLE 抓给你台
Thread.yield();
printState(Thread.currentThread());
}
});
printState(t);
t.start();
printState(t);
// 输出
// STEP: 1, Thread-0's state: NEW
// STEP: 2, Thread-0's state: RUNNABLE
// STEP: 3, Thread-0's state: RUNNABLE
// STEP: 4, Thread-0's state: RUNNABLE
}
}
从就绪状态的线程中选择一个线程,分配给其 CPU 时间片来运行,该线程就进入运行状态。
当线程无法获取锁时,就会进入到阻塞状态。
package org.gettingreal.juc.learning.thread;
/**
* 线程阻塞状态测试
*/
public class ThreadBlockedStateTest extends ThreadStatePrinter {
// 使用对象来模拟一把锁
private static Object lock = new Object();
private static Thread oneThread;
private static Thread anotherThread;
public static void main(String[] args) throws Exception {
oneThread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
oneThread.setName("oneThread");
anotherThread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
anotherThread.setName("anotherThread");
printState(oneThread);
printState(anotherThread);
oneThread.start();
anotherThread.start();
printState(oneThread);
printState(anotherThread);
Thread.sleep(1500);
printState(oneThread);
printState(anotherThread);
Thread.sleep(1500);
printState(oneThread);
printState(anotherThread);
// 输出
// STEP: 1, oneThread's state: NEW
// STEP: 2, anotherThread's state: NEW
// STEP: 3, oneThread's state: TIMED_WAITING
// STEP: 4, anotherThread's state: RUNNABLE
// STEP: 5, oneThread's state: TIMED_WAITING
// STEP: 6, anotherThread's state: BLOCKED
// STEP: 7, oneThread's state: TERMINATED
// STEP: 8, anotherThread's state: TIMED_WAITING
}
}
可以看到在第六步时,anotherThread 的状态是 BLOCKED,因为该线程也需要获得 lock 锁才能执行 synchronized 块里面的逻辑。
处于等待状态的线程不会被分配 CPU 执行时间,要被显式地唤醒(调用 notify() 方法),否则会处于一直等待的状态。调用 wait() 方法会释放锁,线程会进入等待锁池中。
package org.gettingreal.juc.learning.thread;
/**
* 线程等待状态测试
*/
public class ThreadWaitingStateTest extends ThreadStatePrinter {
private static final Object lock = new Object();
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
// 调用 wait 方法进入等待状态
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
printState(t);
t.start();
printState(t);
// 睡眠 1 秒钟,让 t 线程的 wait() 方法得到执行
Thread.sleep(1000);
printState(t);
// 通知线程 t
synchronized (lock) {
lock.notify();
printState(t);
}
// 输出
// STEP: 1, Thread-0's state: NEW
// STEP: 2, Thread-0's state: RUNNABLE
// STEP: 3, Thread-0's state: WAITING
// STEP: 4, Thread-0's state: BLOCKED
}
}
在线程上调用 wait() 方法,该线程会进入 WAITING 状态,释放 lock 锁。后面的 BLOCKED 状态是因为执行完 notify() 方法之后,线程需要再次获取 lock 锁。
线程处于超时等待状态的不会被分配 CPU 执行时间,也无需被唤醒,当然也不会释放对应的锁。
package org.gettingreal.juc.learning.thread;
/**
* 线程超时等待状态测试
*/
public class ThreadTimedWaitingStateTest extends ThreadStatePrinter {
private static final Object lock = new Object();
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
printState(t);
t.start();
printState(t);
// 睡眠 1 秒钟,让 t 线程的 wait() 方法得到执行
Thread.sleep(1000);
printState(t);
// 输出
// STEP: 1, Thread-0's state: NEW
// STEP: 2, Thread-0's state: RUNNABLE
// STEP: 3, Thread-0's state: TIMED_WAITING
}
}
线程的 run() 方法执行完成后,该线程的状态就是终止状态了。
package org.gettingreal.juc.learning.thread;
/**
* 线程终止状态测试
*/
public class ThreadTerminatedStateTest extends ThreadStatePrinter {
private static final Object lock = new Object();
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
}
});
printState(t);
t.start();
printState(t);
// 睡眠 1 秒钟,让 t 线程的 wait() 方法得到执行
Thread.sleep(1000);
printState(t);
// 输出
// STEP: 1, Thread-0's state: NEW
// STEP: 2, Thread-0's state: RUNNABLE
// STEP: 3, Thread-0's state: TERMINATED
}
}