Thread類中有一個已經廢棄的 stop() 方法,它能夠終止線程,但因爲它無論三七二十一,直接終止線程,因此被廢棄了。好比,當線程被中止後還須要進行一些善後操做(如,關閉外部資源),使用這個方法就無能爲力了。能夠經過線程中斷來實現線程終止。java
首先來看一下Java線程中斷的一些內容:ide
由上面幾點和第二句加粗的話可知,可使用線程中斷來實現線程終止,只要目標線程判斷一下中斷標記便可,即便被中斷的線程正處於阻塞狀態,也能把他喚醒起來終止;由第一句加粗的話可知,直接使用線程中斷實現線程終止是存在風險的,由於可能調用了一些Java標準庫的阻塞方法,而致使了中斷標記被清空,也就沒法得到中斷標記了(老是false),所以須要本身建立一箇中斷標記配合使用。測試
如,下面是一個可中斷的任務執行器,他會在每次執行任務前,判斷一下自定i的終止標記和剩餘的任務數(善後);提供的shutdown方法除了將工做線程中斷外(主要做用是喚醒可能處於阻塞狀態的任務),還會將終止交集 terminated 置爲 true。this
執行 main 方法,能夠發現,首先會打印出「客戶端調用了 shutdown 方法」,而後過了四秒,main線程纔會終止,可知shutdown方法正確地將目標線程終止了。關於「按照慣例,Java標準庫中拋出InterruptedException異常的和線程相關的阻塞方法會清空中斷標記」,能夠將條件中的 !interminated 替換成 !Thread.currentThread().isInterrupted(),而後再執行main方法測試,能夠發現main線程始終沒法終止,由於 sleep() 方法清空了中斷標記,因此 !Thread.currentThread().isInterrupted() 始終爲true,致使工做線程始終沒法終止。線程
public class TerminableTaskRunner { // 存儲要執行的任務 private final BlockingQueue<Runnable> tasks; // 線程終止標誌 private volatile boolean terminated; // 剩餘的任務數 private final AtomicInteger count; // 實際執行任務的線程 private volatile Thread workThread; public TerminableTaskRunner(int capacity) { this.tasks = new LinkedBlockingDeque<>(capacity); this.count = new AtomicInteger(0); this.workThread = new WorkThread(); workThread.start(); } public void submit(Runnable task) { this.tasks.add(task); this.count.incrementAndGet(); } public void shutdown() { terminated = true; // 線程終止標誌,因爲中斷標誌可能會被覆蓋,因此須要本身建立一個標誌 if (workThread != null) workThread.interrupt(); // 喚醒線程 } private class WorkThread extends Thread { @Override public void run() { Runnable task; try { while (!terminated || tasks.size() >= 1) { task = tasks.take(); try { task.run(); // 可能會清空當前線程的中斷標記,如task.run()在內部調用的阻塞方法拋出了InterruptedException } catch (Throwable e) { e.printStackTrace(); } count.decrementAndGet(); } } catch (InterruptedException e) { // 一旦調用shutdown且tasks.take()阻塞住,就拋出該異常,沒有任務要執行,直接終止 workThread = null; } } } public static void main(String[] args) { TerminableTaskRunner taskRunner = new TerminableTaskRunner(4); for (int i = 0; i < 4; i++) { taskRunner.submit(()->{ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("客戶端調用了 shutdown 方法"); } }); } taskRunner.shutdown(); } }