前言
强制停止一个线程是不安全的,线程什么时候可以停止,只有线程自己知道,我们模拟主线程中启动一个工作线程,主线程睡眠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); } }
在工作线程内启动一个新的守护线程,当主线程停止工作线程时,会向工作线程发中断信号,就算工作线程在执行一个费时的操作,也会被停止。