在Java中,停止一个线程的主要机制是中断,中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何及何时退出。
Thread的stop()方法是一个强制关闭线程的方法,但是现在jdk版本已不再推荐使用。
过时的stop()方法.png
1. JDK对线程中断的支持
jdk中断机制主要涉及三个方法,分别是:
interrupt()方法.png
interrupted()方法.png
isInterrupted()方法.png
-
interrupt()方法是一个实例方法,该方法用于设置当前线程对象的中断标识位。
interrupt()方法源码.png -
interrupted()方法用于判断当前线程是否被中断,并且该方法调用结束的时候会清空中断标识位。
interrupted()方法会清空中断标识位.png -
isInterrupted是一个实例方法,主要用于判断当前线程对象的中断标志位是否被标记了,如果被标记了则返回true表示当前已经被中断,否则返回false。
isInterrupted()方法不会清空中断标识位.png
2. interrupt()方法详解
先来看一下官方文档的描述:
interrupt()方法官方描述.png简单概括起来就是:只有当线程处于阻塞状态时(包括调用wait()、sleep()、join方法时),能够捕获到[
InterruptedException
],中断标识位将被清空。如果不是处于阻塞状态,即使线程被打断,线程仍会继续执行,因为只是改变了状态标识位,线程无法获得打断信号。
public class ThreadInterupt {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
// 即使被中断也会继续执行
}
});
t1.start();
System.out.println(t1.isInterrupted());
// 中断线程
t1.interrupt();
System.out.println(t1.isInterrupted());
}
}
public class ThreadInterupt {
public static void main(String[] args) {
Object MONITOR = new Object();
Thread t1 = new Thread(() -> {
while (true) {
try {
// 1. Thread.sleep(1000);
// 2. wait()
synchronized (MONITOR) {
MONITOR.wait(10);
}
} catch (InterruptedException e) {
System.out.println("收到打断信号");
System.out.println("线程" + Thread.interrupted());
e.printStackTrace();
}
}
});
t1.start();
System.out.println(t1.isInterrupted());
// 中断线程
t1.interrupt();
System.out.println(t1.isInterrupted());
}
}
3. 采用优雅的方式结束线程的生命周期
3.1 通过开关判断是否关闭线程
public class ThreadCloseGraceful {
static class Worker extends Thread {
// 线程关闭标志
private volatile boolean start = true;
@Override
public void run() {
while (start) {
}
}
public void shutdown() {
this.start = false;
}
}
public static void main(String[] args) {
Worker worker = new Worker();
worker.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 关闭线程
worker.shutdown();
}
}
3.2 通过Thread.interrupted()静态方法判断是否while循环
public class ThreadCloseGraceful2 {
static class Worker extends Thread {
@Override
public void run() {
while (true) {
// 如果线程被打断,退出while循环,执行其它代码
if (Thread.interrupted())
break;
}
// 执行其它代码...
}
}
public static void main(String[] args) {
ThreadCloseGraceful.Worker worker = new ThreadCloseGraceful.Worker();
worker.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
worker.interrupt();
}
}
以上两种方式都是采取while循环+变量判断的方式决定线程是否继续执行,但是存在一个问题,假入代码在while循环中blocked,可能是网络原因,可能是加载一个大的资源,总之被hang住了,那么下一次变量判断没有执行的机会,那么线程就不能关闭。所以还是需要一个强制关闭线程的方法,stop()方法已经过时,不推荐使用。
public class ThreadCloseGraceful2 {
static class Worker extends Thread {
@Override
public void run() {
while (true) {
// load a heavy resource,hang...
// if statement mo chance to execute
// 如果线程被打断,退出while循环,执行其它代码
if (Thread.interrupted())
break;
}
// 执行其它代码...
}
}
public static void main(String[] args) {
ThreadCloseGraceful.Worker worker = new ThreadCloseGraceful.Worker();
worker.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
worker.interrupt();
}
}
4. 强制关闭线程的解决方案
我们都知道创建守护线程的线程结束生命周期,那么守护线程也结束自己的生命周期,利用这一特性,可以设计以下方案。
利用一个类包装执行线程的方法和关闭线程的方法:
public class ThreadService {
// 执行线程
private Thread executeThread;
// 执行线程是否执行完毕标记
private boolean finished = false;
public void execute(Runnable target) {
executeThread = new Thread() {
@Override
public void run() {
// 任务交给一个守护线程运行
Thread runner = new Thread(target);
runner.setDaemon(true);
runner.start();
try {
runner.join(); // 执行线程executeThread在此阻塞,等待守护线程消亡
finished = true;
} catch (InterruptedException e) {
}
}
};
executeThread.start();
}
public void shutdown(long mills) {
long currentTime = System.currentTimeMillis();
while (!finished) {
if ((System.currentTimeMillis() - currentTime) >= mills) {
System.out.println("任务超时,需要结束");
// 这里打断执行线程,执行线程catch到InterruptedException,不再阻塞
// 执行线程消亡,守护线程随着执行线程一块消亡
// 从而达到强制关闭线程的目的
// Java Api没有强制关闭线程的方法,有一个Thread.stop()方法已经过时
executeThread.interrupt();
break;
}
try {
executeThread.sleep(1);
} catch (InterruptedException e) {
System.out.println("执行线程被打断");
break;
}
}
finished = false;
}
}
主线程调用执行线程和关闭线程的方法:
public class ThreadStopFoece {
public static void main(String[] args) {
ThreadService service = new ThreadService();
long startTime = System.currentTimeMillis();
service.execute(() -> {
// 用一个线程执行任务,某种原因使得线程hang住了,无法退出,需要手动关闭
while (true) {
}
});
// 手动关闭,指定关闭前运行时间
service.shutdown(10000);
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
}
这个方案使用到了几个知识点:
- 守护线程随着父线程的消亡而消亡;
- join方法: Waits for this thread to die.这里守护线程join执行线程,导致执行线程阻塞,等待守护线程执行完毕;
- 调用join方法可以捕获到InterruptedException ,shutdown方法里interrupt执行线程,执行线程中断,从而守护线程也结束。
网友评论