一、线程状态
1.1 线程状态概述
线程一共有6种状态,由java.lang.Thread.State类定义
- New:通过new创建线程而未启动的状态
- Runnable:可运行状态,等待CPU调度
- Blocked:
- Waiting:非超时方式的线程等待状态(Object.wait、Thread.join、LockSupport.park等方式),需要由其他线程发通知来唤醒。
- Timed Waiting:超时方式的线程等待状态(Thread.sleep、Object.wait、Thread.join、LockSupport.parkNanos、LockSupport.parkUtil),超时或由其他线程唤醒后,重新进入Runnable状态。
- Terminated:线程终止状态,线程正常执行完成或遇到异常时将进入此状态。
1.2 线程状态转换
这6种状态的互相转化关系,如下图所示:
1.3 小试牛刀:在不同的情况下查看线程状态
当前案例将在main线程中读取各子线程的状态(Runnable状态将通过子线程自行查看)
1.3.1 源码
public class Demo02 {
public static void main(String[] args) throws Exception {
// 第一种状态切换 - 新建 -> 运行 -> 终止
System.out.println("== 第一种状态切换 - 新建 -> 运行 -> 终止 ==");
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread1当前状态:" + Thread.currentThread().getState().toString());
System.out.println("thread1 执行了");
}
});
System.out.println("未启动线程,thread1当前状态:" + thread1.getState().toString());
thread1.start();
Thread.sleep(2000L); // 等待thread1执行结束,再看状态
System.out.println("等待两秒,再看thread1当前状态:" + thread1.getState().toString());
// thread1.start(); // 线程终止之后,再进行调用,会抛出IllegalThreadStateException异常
System.out.println("== 第二种:新建 -> 运行 -> 等待 -> 运行 -> 终止(sleep方式) ==");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {// 将线程2移动到等待状态,1500后自动唤醒
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2当前状态:" + Thread.currentThread().getState().toString());
System.out.println("thread2 执行了");
}
});
System.out.println("未启动线程,thread2当前状态:" + thread2.getState().toString());
thread2.start();
System.out.println("启动线程,thread2当前状态:" + thread2.getState().toString());
Thread.sleep(200L); // 等待200毫秒,再看状态
System.out.println("等待200毫秒,再看thread2当前状态:" + thread2.getState().toString());
Thread.sleep(3000L); // 再等待3秒,让thread2执行完毕,再看状态
System.out.println("等待3秒,再看thread2当前状态:" + thread2.getState().toString());
System.out.println();
System.out.println("== 第三种:新建 -> 运行 -> 阻塞 -> 运行 -> 终止 ==");
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (Demo02.class) {
System.out.println("thread3当前状态:" + Thread.currentThread().getState().toString());
System.out.println("thread3 执行了");
}
}
});
// 主线程持有字节码锁后,启动thread3
synchronized (Demo02.class) {
System.out.println("未启动线程,thread3当前状态:" + thread3.getState().toString());
thread3.start();
System.out.println("启动线程,thread3当前状态:" + thread3.getState().toString());
Thread.sleep(200L); // 等待200毫秒,再看状态
System.out.println("等待200毫秒,再看thread3当前状态:" + thread3.getState().toString());
}
Thread.sleep(3000L); // 再等待3秒,让thread3执行完毕,再看状态
System.out.println("等待3秒,让thread3抢到锁,再看thread3当前状态:" + thread3.getState().toString());
}
}
执行结果:
== 第一种状态切换 - 新建 -> 运行 -> 终止 ==
未启动线程,thread1当前状态:NEW
thread1当前状态:RUNNABLE
thread1 执行了
等待两秒,再看thread1当前状态:TERMINATED
== 第二种:新建 -> 运行 -> 等待 -> 运行 -> 终止(sleep方式) ==
未启动线程,thread2当前状态:NEW
启动线程,thread2当前状态:RUNNABLE
等待200毫秒,再看thread2当前状态:TIMED_WAITING
thread2当前状态:RUNNABLE
thread2 执行了
等待3秒,再看thread2当前状态:TERMINATED
== 第三种:新建 -> 运行 -> 阻塞 -> 运行 -> 终止 ==
未启动线程,thread3当前状态:NEW
启动线程,thread3当前状态:RUNNABLE
等待200毫秒,再看thread3当前状态:BLOCKED
thread3当前状态:RUNNABLE
thread3 执行了
等待3秒,让thread3抢到锁,再看thread3当前状态:TERMINATED
二、线程中止
2.1 不正确的线程中止方式thread.stop()
以简单粗暴的方式中止目标线程,如果是一系列原子的操作,执行到一半,所在线程被调用了thread.stop()方法,将丢失原子性(执行到一半直接退出)
2.2 正确的线程中止方式thread.interrupt()
大多数情况下,只是改变目标线程的中断状态,需要由开发者自行获取该状态以判断目标线程是否中止,如果中止的话,需要执行哪些操作。
如果目标线程在调用wait()、join()或sleep()方法时被阻塞,则会抛出InterruptException异常。
如果目标线程是被IO或NIO的Channel阻塞,则IO操作会被中断或返回特殊异常值,以中止该线程。
2.3 小试牛刀:分别使用不正确、正确的方式中止线程
2.3.1 不正确的方式中止线程thread.stop()
public class Demo03 {
static class StopThread extends Thread {
private int i = 0, j = 0;
@Override
public void run() {
synchronized (this) {
// 增加同步锁,确保线程安全
++i;
try {
// 休眠10秒,模拟耗时操作
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
++j;
}
}
public void print() {
System.out.println("i=" + i + " j=" + j);
}
}
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
// 休眠1秒,确保i变量自增成功
Thread.sleep(1000);
// 暂停线程
thread.stop(); // 错误的终止
while (thread.isAlive()) {
// 确保线程已经终止
} // 输出结果
thread.print();
}
}
执行结果:i、j的数值不一致(可以看到丢失了操作的原子性)
i=1 j=0
2.3.2 正确的方式中止线程thread.interrupt()
……
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
// 休眠1秒,确保i变量自增成功
Thread.sleep(1000);
// 暂停线程
// thread.stop(); // 错误的终止
thread.interrupt(); // 正确终止
while (thread.isAlive()) {
// 确保线程已经终止
} // 输出结果
thread.print();
}
……
执行结果:i、j的数值一致(可以看到保留了操作的原子性)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at cn.zephyr.ch1.Demo03$StopThread.run(Demo03.java:21)
i=1 j=1