对于线程的停止,通常情况下我们是不会去手动去停止的,而是等待线程自然运行至结束,但在实际开发中,很多情况中需要我们提前去手动来停止线程,比如程序中出现异常错误、使用者关闭程序等情况中。如果不能很好地停止线程那么可能会导致各种问题,所以正确的停止线程是非常的重要的,常见的中断线程的方式有以下几种:
1、方式一:使用 Thread 类的 stop() 方法来终止线程(废弃):
Thread 类的 stop() 方法虽然可以终止线程,但该方法已被标识为废弃方法,原因是 stop() 方法太过暴力,即使线程只执行一半,也会被强行终止,不能保证线程资源正确释放,线程不安全,从而产生不可预料的结果,因此不提倡使用。
2、方式二:根据 volatile 修饰的标志位判断线程是否需要中断:
public static class ChangeObjectThread extends Thread
{
// 表示是否停止线程
private volatile boolean stopMe = true;
public void stopMe() {
stopMe = false;
}
@Override
public void run() {
while (!stopMe) {
System.out.println("I'm running");
}
}
}
在上面的代码里面,定义了一个标记变量 stopMe,用于标识线程是否需要退出,当 stopMe() 方法被调用时,stopMe 就会被赋值为 false,此时在代码里面的 while(!stopMe) 就会检测到这个改动,线程就退出了
3、方式三:通过 interrupt 中断机制终止线程:
该方式的核心就是通过 interrupt() 方法设置线程的中断标志位,并通过 isInterrupt()/interrupted() 方法监视并判断中断信号,当线程检测到为 true 时则说明接收到中断信号,此时需要被中断线程做相应的处理。但如何去响应这个中断信号,被中断线程有完全的自主权,也就是中断结果是死亡或是继续运行,取决于这个被中断线程本身的逻辑。
- Thread.interrupte():设置线程的中断标志位为 true,表示被其他线程进行了中断操作,但它不会像 stop() 方法那样强制中断正在运行的线程,仅仅起到通知被停止线程的作用;而被中断线程,则需要通过监视自身的标志位是否被中断来进行响应,比如使用 isInterrupted() 或 interrupted() 方法来判断是否被中断;
- this.interrupted():测试当前线程是否已经中断。如果连续两次调用该方法,第一次返回 true,第二次返回false,因为 interrupted() 方法具有清除状态的功能,它内部实现是调用的当前线程的 isInterrupted(),并且会重置当前线程的中断状态。
- this.isInterrupted():测试线程是否已经中断,但是不会清除状态标识
3.1、使用 interrupt() + isInterrupted() 来中断线程:
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while (true){
System.out.println("t1线程还在运行......");
Thread current = Thread.currentThread();
boolean interrupted = current.isInterrupted(); //默认值是false
if (interrupted){ //如果外部调用中断 那么会将该标记改为true
System.out.println("接受到中断标记....."+interrupted+"开始中断线程.....");
current.interrupted();//再次调用interrupted测试复位标记 会重置清除标记 注意是带有ed 不是interrupt()方法
System.out.println("经过interrupted复位后标记为:"+current.isInterrupted());
break;
}
}
},"t1");
thread.start();
thread.interrupt(); //设置线程中断
}
//运行结果
t1线程还在运行......
接受到中断标记.....true开始中断线程.....
经过interrupted复位后标记为:false
3.2、使用 interrupt() + InterruptedException 来中断线程:
Thread thread2=new Thread(()->{
while (true){
System.out.println("t2线程还在运行......");
Thread current = Thread.currentThread();
if (current.isInterrupted()){ //判断当前线程是否中断
System.out.println("接受到中断标记....."+Thread.currentThread().isInterrupted());
// break;
}
try {
TimeUnit.SECONDS.sleep(5); //5s睡眠
} catch (InterruptedException e) {
e.printStackTrace();
//因为抛出异常后会将线程标记恢复为 默认值false
System.out.println("Thread.currentThread().isInterrupted() = " + Thread.currentThread().isInterrupted());
break;
}
}
},"t2");
thread2.start();
thread2.interrupt();
}
t2线程还在运行......
接受到中断标记.....true
Thread.currentThread().isInterrupted() = false
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at java.base/java.lang.Thread.sleep(Thread.java:339)
at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
at com.java.thread.TestInterrupt.lambda$main$0(TestInterrupt.java:40)
at java.base/java.lang.Thread.run(Thread.java:834)
从上面的执行结果可以证明,当线程处于阻塞状态(Sleep、wait、join)时,也是可以感受到中断通知并抛出异常的,所以不用担心长时间休眠中线程感受不到中断了。
对于线程的停止,最正确最优雅的方式就是通过 interrupt() 的方式来实现,但 interrupt() 仅起到通知被停止线程的作用,对被停止的线程而言,它拥有完全的自主权,既可以立即停止,也可以选择一段时间后停止,也可以选择不停止。比如抛出 InterruptedException 后,再次中断设置,让程序能后续继续进行终止操作;也比如线程在进行 IO 操作时,比如写文件操作,这时接收到终止线程的信号,那么它不会立马停止,而是根据自身业务来判断该如何处理,是将整个文件写入成功后再停止还是不停止都取决于被通知线程的处理,因为如果立马终止线程就可能造成数据的不完整性,这并不是业务所不希望的结果。那么可能很多读者会疑惑,既然这样那存在的意义有什么呢,其实是因为对于 Java 而言,就是期望程序之间是能够相互通知、协作的管理线程