文章目录
线程状态
一、线程的6种状态
线程的状态在java.lang.Thread.State中有明确的定义
- New:尚未启动的线程的线程状态。
- Runnable:可运行的线程状态,等待CPU调度。
- Blocked:线程阻塞等待监视器锁定的线程状态(处于synchronized同步代码块或方法中被阻塞)。
- Waiting:等待线程的线程状态(不带超时的方式:Object.wait、Thread.join、LockSupport.park)。
- Timed waiting:具有指定等待时间的等待线程的线程状态(带超时的方式:Object.wait、Thread.sleep、Thread.join、LockSupport.parhNanos、LockSupport.parkUntil)。
- Terminated:终止的线程的线程状态(线程正常完成执行或者出现异常)。
二、线程状态之间的关系
三、结合代码演示线程状态之间的变更
/** * 示例2 - 多线程运行状态切换示例 <br/> */ public class Demo2 { public static Thread thread1; public static Demo2 obj; 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());//runnable System.out.println("thread1 执行了"); } }); System.out.println("没调用start方法,thread1当前状态:" + thread1.getState().toString());//new thread1.start(); Thread.sleep(2000L); // 等待thread1执行结束,再看状态 System.out.println("等待两秒,再看thread1当前状态:" + thread1.getState().toString());//terminated // thread1.start(); TODO 注意,线程终止之后,再进行调用,会抛出IllegalThreadStateException异常 System.out.println(); 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());//runnable System.out.println("thread2 执行了"); } }); System.out.println("没调用start方法,thread2当前状态:" + thread2.getState().toString());//new thread2.start(); System.out.println("调用start方法,thread2当前状态:" + thread2.getState().toString());//runnable Thread.sleep(200L); // 等待200毫秒,再看状态 System.out.println("等待200毫秒,再看thread2当前状态:" + thread2.getState().toString());//timed_waitting Thread.sleep(3000L); // 再等待3秒,让thread2执行完毕,再看状态 System.out.println("等待3秒,再看thread2当前状态:" + thread2.getState().toString());//terminated System.out.println(); System.out.println("############第三种:新建 -> 运行 -> 阻塞 -> 运行 -> 终止###########################"); Thread thread3 = new Thread(new Runnable() { @Override public void run() { System.out.println("线程三进入线程"); synchronized (Demo2.class) { System.out.println("拿到锁了"); System.out.println("thread3当前状态:" + Thread.currentThread().getState().toString());//runnable System.out.println("thread3 执行了"); } } }); synchronized (Demo2.class) { System.out.println("没调用start方法,thread3当前状态:" + thread3.getState().toString());//new thread3.start(); System.out.println("调用start方法,thread3当前状态:" + thread3.getState().toString());//runnable Thread.sleep(200L); // 等待200毫秒,再看状态 System.out.println("等待200毫秒,再看thread3当前状态:" + thread3.getState().toString());//blocked } Thread.sleep(3000L); // 再等待3秒,让thread3执行完毕,再看状态 System.out.println("等待3秒,让thread3抢到锁,再看thread3当前状态:" + thread2.getState().toString());//terminated } }
控制台打印结果
#######第一种状态切换 - 新建 -> 运行 -> 终止################################ 没调用start方法,thread1当前状态:NEW thread1当前状态:RUNNABLE thread1 执行了 等待两秒,再看thread1当前状态:TERMINATED ############第二种:新建 -> 运行 -> 等待 -> 运行 -> 终止(sleep方式)########################### 没调用start方法,thread2当前状态:NEW 调用start方法,thread2当前状态:RUNNABLE 等待200毫秒,再看thread2当前状态:TIMED_WAITING thread2当前状态:RUNNABLE thread2 执行了 等待3秒,再看thread2当前状态:TERMINATED ############第三种:新建 -> 运行 -> 阻塞 -> 运行 -> 终止########################### 没调用start方法,thread3当前状态:NEW 调用start方法,thread3当前状态:RUNNABLE 线程三进入线程 等待200毫秒,再看thread3当前状态:BLOCKED 拿到锁了 thread3当前状态:RUNNABLE thread3 执行了 等待3秒,让thread3抢到锁,再看thread3当前状态:TERMINATED
线程终止
一、不正确的程线终止 - Stop
Stop:中止线程,并且清除监控器锁的信息,但是可能导致线程安全问题,JDK不建议使用并标记为废弃Deprecated
下面结合代码演示stop方法为什么会导致线程安全问题
a、新建类StopThread
public 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;
}
}
/** * 打印i和j */
public void print() {
System.out.println("i=" + i + " j=" + j);
}
}
b、新建Demo3测试类
/**
* 示例3 - 线程stop强制性中止,破坏线程安全的示例
*/
public class Demo3 {
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();
}
}
c、控制台输出结果
i=1 j=0
理论上来讲代码的执行结果应该是 i和j的结果应该是一致的,所以由此可以看到stop方法是直接进入了同步代码块中停止了线程,这中结果是违背了设计这个程序的初衷的,所以说stop方法会导致线程安全问题。
二、正确的程线终止(1) - interrupt
- 如果目标线程在调用Object class 的wait()、wait(long) 或wait(long, int) 方法、join()、join(long, int)或sleep(long, int)方法时被阻塞,那么Interrupt会生效, 该线程的中断状态将被清除,抛出InterruptedException异 常。
- 如果目标线程是被I/O或者NIO中的Channel所阻塞,同样,I/O操作会被中断或者返回特殊异常值。达到终止线程的目的。
- 如果以上条件都不满足,则会设置此线程的中断状态。
把Demo3测试类中的stop方法改成interrupt方法后控制台的输出结果
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at StopThread.run(StopThread.java:11)
i=1 j=1
可以看到结果是正确的,但是抛出了一个异常,这个异常是sleep方法抛出的 ,开发者可以捕获这个异常自行处理。
三、正确的程线终止(2) - 标志位
就是在代码逻辑中,增加一个判断,用来控制线程执行的终止
下面结合代码演示
/** 通过标志位来判断 */
public class Demo4 extends Thread {
public volatile static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
try {
while (flag) { // 判断是否运行
System.out.println("运行中");
Thread.sleep(1000L);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// 3秒之后,将状态标志改为False,代表不继续运行
Thread.sleep(3000L);
flag = false;
System.out.println("程序运行结束");
}
}
控制台输出结果
运行中
运行中
运行中
程序运行结束
可以看到这种方式也是非常优雅的