前言
进入正题之前还是先回顾一个会被问无数遍的问题:
- stop和interrupt的区别是什么?
简单的说就是,stop的话,那线程就真的结束了。
interrupt仅仅只是给线程标记了一下,告诉你说当前线程可以结束了,你需要自己进行结束线程。如果你自己不做处理,那这个方法调用了并看不出效果。
栗子一:不带sleep等中断抛异常方法的使用
上代码:
public class TestClassSenseOne {
public static void main(String[] args) throws InterruptedException {
TestThread t = new TestThread();
//开启子线程执行
t.start();
//中断子线程
t.interrupt();
//等待子线程执行完毕,t.join() 可以这么理解,谁.join() 谁阻塞当前线程先执行自己
t.join();
System.out.println("主线程结束");
}
}
class TestThread extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
//没有被标记为中断状态就执行,上面start后直接调用中断,这句是不会输出的
System.out.println("我没有被中断");
}
System.out.println("啊——————————————————————————————断了。");
}
}
运行结果:
啊——————————————————————————————断了。
主线程结束
说明:
上面这段代码就是,interrupt()的基本用法,子线程TestThread会一直判断当前线程有没有被标记为可以中断了,直到检查到被标记了,while循环结束,子线程任务结束。
栗子二:带sleep的使用
public class TestClassSenseTwo {
public static void main(String[] args) throws InterruptedException {
TestThreadTwo t = new TestThreadTwo();
//开启子线程执行
t.start();
//模拟真实任务,其实执行5秒就执行完了,再继续等待到20s毫无意义
Thread.sleep(5000);
System.out.println("模拟真实任务,其实执行5秒就执行完了,再继续等待到20s毫无意义");
//所以在这里中断子线程
t.interrupt();
//等待子线程执行完毕,t.join() 可以这么理解,谁.join() 谁阻塞当前线程先执行自己
t.join();
System.out.println("主线程结束");
}
}
class TestThreadTwo extends Thread {
@Override
public void run() {
try {
System.out.println("开始执行20秒的任务");
Thread.sleep(20000);
System.out.println("线程任务执行完毕");
} catch (InterruptedException e) {
System.out.println("线程在sleep的时候被中断。");
return;
}
}
}
运行结果:
开始执行20秒的任务
模拟真实任务,其实执行5秒就执行完了,再继续等待到20s毫无意义
线程在sleep的时候被中断。
主线程结束
可以看到,这里了结束子线程的操作输出是在 catch块中做的,因为sleep被中断会抛出InterruptedException异常,同时也会清除当前线程被标记的 可被结束状态,如果不在catch块中结束,那么interrupt的调用并不能提前结束子线程。
大栗子:场景下使用的例子
- 描述:
我在程序启动时有一个检查菜单的任务,这个任务需要一直被执行,直到检查成功。 - 思路:
我先定义一个任务线程类,在其中while (!Thread.currentThread().isInterrupted())
执行检查菜单方法,当检查菜单方法使用interrupt中断任务时说明任务完成。
上代码:
检查方法的Service接口
public interface TestService {
void checkMenu() throws InterruptedException;
}
实现类
public class TestServiceImpl implements TestService {
@Override
public void checkMenu() throws InterruptedException {
System.out.println("检查菜单----------->");
//模拟随机成功与失败
if (Math.random() * 10 > 4) {
//模拟执行检查任务需要3秒耗时
Thread.sleep(3000);
//使用中断的方式,结束任务。
System.out.println("检查任务完成******************,checkMenu() 使用interrupt()结束任务");
Thread.currentThread().interrupt();
}
}
}
任务类
class DoMenuCheckTask extends Thread {
@Override
public void run() {
TestService testService = new TestServiceImpl();
//如果当前线程没有被中断,说明 checkMenu 没有真正成功
while (!Thread.currentThread().isInterrupted()) {
try {
testService.checkMenu();
/*
下面这两行的开启与关闭,便是两种结束当前任务的方式
1. 这里不使用sleep ,任务正常被 interrupt 结束
2. 这里使用 sleep, interrupt打断的就是 sleep(),打断sleep会清除isInterrupted中断状态,
并抛出 java.lang.InterruptedException: sleep interrupted 异常,所以,不在catch块中结束任务,
任务便无法结束了。
*/
// System.out.println("菜单检查没有通过中断完成任务,休眠5秒后继续执行----------");
// //如果这里使用了 sleep 那么任务便是通过下面catch块中结束。
// Thread.sleep(5000);
} catch (InterruptedException e) {
//被中断,任务结束
e.printStackTrace();
System.out.println("菜单检查通过中断interrupt成功完成----------->>>>>>>>");
return;
}
}
}
}
执行
public class SceneExample {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始执行----------->>>>>>>>");
DoMenuCheckTask task = new DoMenuCheckTask();
//开始菜单检查执行任务
task.start();
//让检查任务先执行
task.join();
System.out.println("主线程结束执行----------->>>>>>>>");
}
}
不使用sleep输出结果:
主线程开始执行----------->>>>>>>>
检查菜单----------->
检查任务完成******************,checkMenu() 使用interrupt()结束任务
主线程结束执行----------->>>>>>>>
Process finished with exit code 0
使用sleep的输出结果:
主线程开始执行----------->>>>>>>>
检查菜单----------->
检查任务完成******************,checkMenu() 使用interrupt()结束任务
菜单检查没有通过中断完成任务,休眠5秒后继续执行----------
菜单检查通过中断interrupt成功完成----------->>>>>>>>
主线程结束执行----------->>>>>>>>
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.kusch.demo.test.example.DoMenuCheckTask.run(SceneExample.java:47)
Process finished with exit code 0