结束线程我们常用的有两种方式,一种是通过interrupted方法判断线程是否处于打断状态,还有一种是设置一个标志位,如下
interrupted
package forceclose;
public class MyThread extends Thread {
@Override
public void run() {
while(true){
if(Thread.interrupted())
break;
}
}
}
标志位
package forceclose;
//标志位方式
public class MyThread extends Thread {
private boolean flag = true;
@Override
public void run() {
while(flag){
}
}
public void shutdown(){
flag = false;
}
}
当我们要结束线程时,调用shutdown方法修改标志位即可。
然而在实际多线程开发中,线程如果在长时间执行读写等操作,会因为阻塞,不能够读取判断状态或者flag标志位,为了避免任务超时,我们就需要立即暴力结束线程。结合多线程API,可以利用守护线程来完成这么一个需求。
关于守护线程
什么是守护线程,说白了就是一个死循环线程,它是为了用户线程服务(我们自定义的线程)服务,如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了,所以会被强行终止。最常见的守护线程如GC,就是守护线程,一直在后台为我们的程序服务。当我们的程序结束,GC也会自动结束。这里只是一个简单介绍,关于守护线程更深入的理解请自行查阅相关资料。
案例
利用守护线程,我们就可以完成一个线程的暴力终止案例,我们可以启动一个执行线程,在执行线程中开启一个守护线程,然后在将具体的执行逻辑放在守护线程中,通过对执行线程的控制来达到控制执行具体功能的线程。具体代码如下:
package forceclose;
public class ThreadService {
private Thread excutethread;
private boolean finish = false;
public void Excute(final Runnable task){
excutethread = new Thread(){
@Override
public void run() {
//task是我们要执行的任务逻辑
Thread runner = new Thread(task);
runner.setDaemon(true);
runner.start();
try {
//防止runner还没有启动,执行线程就结束了,调用join,执行线程会一直阻塞直到守护线程执行完毕
runner.join();
//说明任务已经执行完毕
finish = true;
} catch (InterruptedException e) {
}
}
};
excutethread.start();
}
//mills 最大等待超时时间
public void shutdown(long mills){
long startTime = System.currentTimeMillis();
//一直循环判断是否结束或者任务是否超时
//如果任务提前结束,无需等待
while(!finish){
if(System.currentTimeMillis()-startTime>mills){
System.out.println("任务超时,强行结束!");
excutethread.interrupt();
break;
}
try {
excutethread.sleep(1);
} catch (InterruptedException e) {
System.out.println("线程被打断!");
break;
}
}
finish =false;
}
}
package forceclose;
public class ThreadCloseForce {
public static void main(String[] args) {
ThreadService service = new ThreadService();
long StartTime = System.currentTimeMillis();
service.Excute(new Runnable() {
public void run() {
//死循环模拟线程正在执行读写任务
while(true){
}
}
});
//最多执行十秒
service.shutdown(10000);
long endTime = System.currentTimeMillis();
System.out.println(endTime-StartTime);
}
}
运行,我们可以看到结果。
线程执行有延迟,所以不一定会精确到十秒,不会相差太多。