java中如何优雅的停止一个线程
前言
强制停止一个线程是不安全的,线程什么时候可以停止,只有线程自己知道,我们模拟主线程中启动一个工作线程,主线程睡眠3秒后去停止工作线程。
标志位
public class ThreadCloseGraceful {
private static class Worker extends Thread {
private volatile boolean running = true;
public Worker(String name) {
super(name);
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while (running) {
System.out.println(threadName + " is running");
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(threadName + " exit");
}
public void shutdown() {
this.running = false;
}
}
public static void main(String[] args) {
Worker worker = new Worker("Worker");
worker.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
worker.shutdown();
}
}
使用volatile关键字保证内存可见性,主线程修改了,工作线程可以立刻知道running字段的内容变化。如果工作线程已经阻塞了,这种方法没有办法停止,这种情况可以使用线程的interrupt()方法。
Thread.interrupt()
public class ThreadCloseGraceful2 {
private static class Worker extends Thread {
public Worker(String name) {
super(name);
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while (true) {
System.out.println(threadName + " is running");
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
System.out.println(threadName + " exit");
}
}
public static void main(String[] args) {
Worker worker = new Worker("Worker");
worker.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
worker.interrupt();
}
}
我们调用interrupt()方法后
- 如果工作线程被阻塞了,会抛出InterruptedException异常,我们可以捕获此异常,然后停止线程
- 如果工作线程在运行中,会将线程的中断标记设置为true,可以调用isInterrupted()方法,判断当前中断标记是否为true。
apache工具包中的FileAlterationMonitor文件变化监控器就是通过标志位和中断来实现的。
强制停止
public class ThreadService {
private Thread executeThread;
private boolean finished = false;
public void execute(Runnable task) {
executeThread = new Thread(() -> {
Thread runner = new Thread(task);
runner.setDaemon(true);
runner.start();
try {
runner.join();
finished = true;
} catch (InterruptedException e) {
//ignore
}
});
executeThread.start();
}
public void shutdown(long mills) {
long currentTime = System.currentTimeMillis();
while (!finished) {
if ((System.currentTimeMillis() - currentTime) >= mills) {
System.out.println("任务超时,需要结束他!");
executeThread.interrupt();
break;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
System.out.println("执行线程被打断!");
break;
}
}
finished = false;
}
}
客户端调用
public class ThreadCloseForce {
public static void main(String[] args) throws InterruptedException {
ThreadService service = new ThreadService();
long start = System.currentTimeMillis();
service.execute(() -> {
//load a very heavy resource.
/*while (true) {
}*/
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
service.shutdown(3000);
System.out.println("main start");
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
在工作线程内启动一个新的守护线程,当主线程停止工作线程时,会向工作线程发中断信号,就算工作线程在执行一个费时的操作,也会被停止。