Estoy participando en el "Programa de Vela · Nuggets"
Use la interrupción para decirle al subproceso que deje de ejecutarse, ¡no lo fuerce a detenerse!
Detener el hilo en caso normal
public class RightWayStopThreadWithoutSleep implements Runnable {
@Override
public void run() {
int num = 0;
while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
if (num % 10000 == 0) {
System.out.println(num + "是1W的倍数");
}
num++;
}
System.out.println("任务运行结束!");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
thread.start();
// 等待1s
Thread.sleep(1000);
// 通知停止线程
thread.interrupt();
}
}
复制代码
Use thread.interrupt() para notificar al hilo que se detenga
Pero los hilos necesitan cooperar :
Use Thread.currentThread().isInterrupted() en while para detectar el estado actual del hilo
resultado de la operación:
……
……
221730000是1W的倍数
221740000是1W的倍数
221750000是1W的倍数
221760000是1W的倍数
221770000是1W的倍数
221780000是1W的倍数
221790000是1W的倍数
221800000是1W的倍数
任务运行结束!
Process finished with exit code 0
复制代码
Detener un hilo si podría estar bloqueado
public class RightWayStopThreadWithSleep {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while (num <= 300 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
}
try {
// 等个1秒,模拟阻塞
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("线程已停止!!");
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
// 等待时间要小于上面设置的1秒,不然线程都运行结束了,才执行到下面的thread.interrupt();代码
Thread.sleep(500);
// 通知停止线程
thread.interrupt();
}
}
复制代码
El subproceso se interrumpe cuando recibe la señal de interrupción durante la suspensión durante 1 segundo.
La forma en que un subproceso responde a una interrupción durante el sueño es lanzar una InterruptedException
resultado de la operación:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
线程已停止!!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:19)
at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 0
复制代码
Detenga el hilo si se bloquea después de cada iteración
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
try {
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
// 每次循环都要等待10毫秒,模拟阻塞
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
// 5秒后通知停止线程
Thread.sleep(5000);
thread.interrupt();
}
}
复制代码
Cuando cada iteración bloquee el subproceso durante un período de tiempo, cuando se juzgue la condición del bucle while/for ,
No es necesario usar * Thread.currentThread().isInterrupted() * para determinar si el hilo está interrumpido
resultado de la operación:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
复制代码
Si pones el try/catch en el código anterior dentro de un bucle while
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
try {
// 每次循环都要等待10毫秒,模拟阻塞
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
// 5秒后通知停止线程
Thread.sleep(5000);
thread.interrupt();
}
}
复制代码
resultado de la operación:
0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:18)
at java.lang.Thread.run(Thread.java:748)
500是100的倍数
600是100的倍数
700是100的倍数
……
……
复制代码
Encontrará que, aunque se lanza una excepción, el programa no se detiene y continúa produciendo,
¡ Incluso si se agrega la condición !Thread.currentThread().isInterrupted() al juicio de la condición while , el programa no se puede detener!
La razón es
java语言在设计 sleep() 函数时,有这样一个理念:
就是当它一旦响应中断,便会把 interrupt 标记位清除。
也就是说,虽然线程在 sleep 过程中收到了 interrupt 中断通知,并且也捕获到了异常、打印了异常信息,
但是由于 sleep 设计理念,导致 Thread.currentThread().isInterrupted() 标记位会被清除,
所以才会导致程序不能退出。
这里如果要停止线程,只需要在 catch 内 再调用一次 interrupt(); 方法
try {
// 每次循环都要等待10毫秒,模拟阻塞
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
复制代码
所以说,不要以为调用了 interrupt() 方法,线程就一定会停止。
两种停止线程最佳方法
1. 捕获了 InterruptedException 之后的优先选择:在方法签名中抛出异常
public class RightWayStopThreadInProd implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
System.out.println("go...");
try {
throwInMethod();
} catch (InterruptedException e) {
// 捕获异常,进行保存日志、停止程序等操作
System.out.println("stop");
e.printStackTrace();
}
}
}
/**
* 如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是try/catch
* 这样调用方才能捕获异常并作出其它操作
* @throws InterruptedException
*/
private void throwInMethod() throws InterruptedException {
Thread.sleep(2000);
}
}
复制代码
如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是 try/catch
这样调用方才能捕获异常并做出其它操作。
2. 在 catch 中调用 Thread.currentThread().interrupt(); 来恢复设置中断状态
public class RightWayStopThreadInProd2 implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd2());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("程序运行结束");
break;
}
reInterrupt();
}
}
private void reInterrupt() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
复制代码
这里的 if (Thread.currentThread().isInterrupted()) 判断,就是要你的代码有响应中断的能力。
总结
- 调用 interrupt 方法不一定会中断线程
- 通知线程停止,线程不会立即停止,而是会在合适的时候停止
- 代码要有响应中断的能力