一、概述
打断线程,你想到了什么?Thread.stop()方法吗?但是这个方法已经被废弃掉了,使用这个方法会产生很多问题。Thread.interrupt()方法呢?遗憾的是,这个方法并不能打断线程,只是提供一个信号量而已,要想通过这个方法来打断线程,还需要自己去判断这个信号量,然后用这几的逻辑来打断线程。
二、interrupt方法抛出异常
在多线程中,有些方法天生接收打断信号量,如果发生打断指令,会抛出异常,比如:sleep(long tiome)、wait(long time) 、join()等。这些方法在执行中,如果被打断,会抛出InterruptedException异常,从而结束这些方法,但是并没有打断线程,线程接着运行后面的逻辑。
public class InterruptThread {
private static Thread thread;
//线程方法
public static void worker() {
thread = new Thread(() -> {
try {
Thread.sleep(500000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程被打断了....");
});
thread.start();
}
public static void main(String[] args) {
worker();
//打断线程
thread.interrupt();
}
}
结果显示,“线程被打断了…”,所以线程并没有真正的被打断。所以,如果将上面的方法改成while(true)循环,按照上面的逻辑,线程是不能被结束掉的。
三、通过interrupt方式打断线程
线程调用interrupt方法打断线程,然后通过方法interrupted方法,可以获取到打断信号。通过这个信号,可以在代码逻辑中停止线程。
public class ThreadInterruptGracefulLearn {
private static class Work extends Thread {
@Override
public void run() {
while (true) {
System.out.println("子线程运行中.....");
if(Thread.interrupted()) {
System.out.println("子线程被打断.....");
break;
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Work work = new Work();
work.start();
//等待子线程运行100毫秒,如果100毫秒没有运行结束,那么就打断线程
work.join(100L);
work.interrupt();
Thread.sleep(1000L);
System.out.println("主线程结束.....");
}
}
四、通过开关的方式打断线程
通过开关的方式打断异常时,需要定义一个volatile标识的变量,通过判断这个变量来打断线程
public class ThreadInterruptGracefulLearn01 {
private static class Work extends Thread {
//注意volatile关键字,保证变量的可见性
private volatile boolean start = true;
@Override
public void run() {
while (start) {
System.out.println("子线程在运行中.....");
}
}
public void shutDown() {
this.start = false;
System.out.println("子线程被停止.....");
}
}
public static void main(String[] args) throws InterruptedException {
Work work = new Work();
work.start();
//主线程等待3秒,3秒之后work还没有执行完成,那么就停止它
work.join(3000L);
if(work.isAlive()) {
work.shutDown();
}
System.out.println("主线程结束.....");
}
}
五、暴力打断线程
将工作线程设置为守护线程,那么主线程结束的是时候,守护线程也就结束了。
/**
* 强制关闭线程:可以将工作线程设置为守护线程,
* 只要主线程结束了,那么守护线程也结束了,
* 算是暴力停止线程的一种方式
*/
public class ForceInterruptThread {
public static void main(String[] args) {
//超时打断
WorkThread workThread1 = new WorkThread();
workThread1.execute(() -> {
while (true){}
});
workThread1.shutdown(3000L);
//正常结束
WorkThread workThread2 = new WorkThread();
workThread2.execute(() -> {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
workThread2.shutdown(3000L);
System.out.println("主线程结束.....");
}
}
class WorkThread {
private Thread executeThread;
//用来判断守护线程中的任务是否运行完成,如果运行结束了,那么可以将主线程打断
private volatile boolean flag = false;
public void execute(Runnable runnable) {
executeThread = new Thread(() -> {
Thread workerThread = new Thread(runnable);
//工作线程设置为守护线程
workerThread.setDaemon(true);
//启动工作线程
workerThread.start();
/*
* 主线程等待工作线程执行完成,使用join()方法,如果在
* join的过程中,线程被打断,那么主线程(executeThread)将结束,
* 当然守护线程也就结束了
*/
try {
workerThread.join();
//守护线程工作完成
flag = true;
System.out.println("任务完成,正常退出......");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executeThread.start();
}
/**
* 超时停止线程,
*/
public void shutdown(long mili) {
//定义开始时间和结束时间
long startTime = System.currentTimeMillis();
long endTime = System.currentTimeMillis();
while (!flag) {
//如果超时,打断主线程
if(endTime - startTime > mili) {
System.out.println("任务超时打断......");
executeThread.interrupt();
break;
}
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
endTime = System.currentTimeMillis();
}
}
}