一、原理介绍:
使用Interrupt来通知停止线程,而不是强制。
在什么情况下会需要用到停止线程?
或许是用户主动取消,或许是突然服务要被快速地关闭,以及在运行超时或者出错的情况下也需要被停止。
Java没有一种机制来安全地停止线程,但是它提供了interrupt,这是一种合作机制,我们用一个线程t1来通知另一个线程t2,让它停止当前的工作,但是被中断的线程本身拥有决定权,它不但能决定何时去响应中断信号,何时去停止,他还拥有最高决定权。也就是说虽然t1通知t2要中断,但是如果t2不想中断,我们也无能为力。也就说我们根本无法强制停止线程。
大多数时候,我们想停止一个线程都会至少让它运行结束。我们认为被通知停止的线程本身更加清除应该如何去执行后续的清理和停止工作,所以我们把决定权交给被通知停止的线程。
所以,停止线程的核心就是如何正确地用interrupt来通知那个线程,以及被停止的线程如何配合。
二、通常线程会在什么情况下停止?
1,run()的逻辑运行完毕
2,抛出异常且方法没有捕获。
线程在停止之后他所占用的资源都会被JVM回收。
三、正确地停止线程:interrupt
1,通常情况停止线程
/**
* 普通情况:
* run()内没有sleep()或wait()的停止线程
*/
public class RightWayStopThreadWithoutSleep implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
int num = 0;
//被停止线程需要配合响应interrupt信号(!Thread.currentThread().isInterrupted())
while(!Thread.currentThread().isInterrupted() && num<Integer.MAX_VALUE){
if (num % 10000 ==0){
System.out.println(num+"是10000的倍数");
}
num++;
}
System.out.println("任务运行结束");
}
}
2,线程可能会被阻塞
线程执行的逻辑可能包含wait(),sleep()
package threadcoreknpwledge.stopthread;
/**
* 线程阻塞的情况:
* run()内有sleep()的停止线程,尝试在线程阻塞等待的期间,中断线程
*/
public class RightWayStopThreadWithSleep {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
try {
while (
//如果还没走到sleep就被中断了,所以这个判断还是需要
!Thread.currentThread().isInterrupted() &&
num <= 300) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
}
//当线程在休眠的过程中收到了中断信号,它便会响应这个中断信号,它响应的方式非常特殊
//它响应的方式就是抛出InterruptedException
Thread.sleep(1000);
//
} catch (InterruptedException e) {
//我们通过处理 InterruptedException 异常来实现当线程处于阻塞过程中,依然能响应中断信号。
//java.lang.InterruptedException: sleep interrupted
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(500);
//主线程向子线程发出中断信号,在子线程阻塞的时候发出中断信号
thread.interrupt();
}
}
3,如果线程在每次迭代(for循环)后都阻塞
package threadcoreknpwledge.stopthread;
/**
* run()在执行过程中,每次循环都有sleep()或wait(),
* 那么我们不需要每次迭代都判断 Thread.currentThread().isInterrupted()
* 因为每次sleep过程中会帮我们判断isInterrupted(),响应中断
*/
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
try {
while (
//在这里,这个判断不需要
// !Thread.currentThread().isInterrupted() &&
num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
// 每次循环都调用 sleep,会在sleep的过程中判断isInterrupted
Thread.sleep(10);
}
//当线程在休眠的过程中收到了中断信号,它便会响应这个中断信号,它响应的方式非常特殊
//它响应的方式就是抛出InterruptedException
} catch (InterruptedException e) {
//我们通过处理 InterruptedException 异常来实现当线程处于阻塞过程中,依然能响应中断信号。
//java.lang.InterruptedException: sleep interrupted
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
//主线程向子线程发出中断信号,在子线程阻塞的时候发出中断信号
thread.interrupt();
}
}