多執行緒:中斷(interrupt、interrupted、executor)
阿新 • • 發佈:2019-01-14
一個執行緒執行完畢之後會自動結束,如果在執行過程中發生異常也會提前結束。
InterruptedException
通過呼叫一個執行緒的 interrupt() 來中斷該執行緒,如果該執行緒處於阻塞、限期等待或者無限期等待狀態,那麼就會丟擲 InterruptedException,從而提前結束該執行緒。但是不能中斷 I/O 阻塞和 synchronized 鎖阻塞。
對於以下程式碼,在 main() 中啟動一個執行緒之後再中斷它,由於執行緒中呼叫了 Thread.sleep() 方法,因此會丟擲一個 InterruptedException,從而提前結束執行緒,不執行之後的語句。
public class InterruptExample { private static class MyThread1 extends Thread { @Override public void run() { try { Thread.sleep(2000); System.out.println("Thread run"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public static void main(String[] args) throws InterruptedException { Thread thread1 = new MyThread1(); thread1.start(); thread1.interrupt(); System.out.println("Main run"); }
Main run java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at InterruptExample.lambda$main$0(InterruptExample.java:5) at InterruptExample$$Lambda$1/713338599.run(Unknown Source) at java.lang.Thread.run(Thread.java:745)
interrupted()
如果一個執行緒的 run() 方法執行一個無限迴圈,並且沒有執行 sleep() 等會丟擲 InterruptedException 的操作,那麼呼叫執行緒的 interrupt() 方法就無法使執行緒提前結束。
但是呼叫 interrupt() 方法會設定執行緒的中斷標記,此時呼叫 interrupted() 方法會返回 true。因此可以在迴圈體中使用 interrupted() 方法來判斷執行緒是否處於中斷狀態,從而提前結束執行緒。
public class InterruptExample { private static class MyThread2 extends Thread { @Override public void run() { while (!interrupted()) { // .. } System.out.println("Thread end"); } } }
public static void main(String[] args) throws InterruptedException { Thread thread2 = new MyThread2(); thread2.start(); thread2.interrupt(); }
Thread end
Executor 的中斷操作
呼叫 Executor 的 shutdown() 方法會等待執行緒都執行完畢之後再關閉,但是如果呼叫的是 shutdownNow() 方法,則相當於呼叫每個執行緒的 interrupt() 方法。
以下使用 Lambda 建立執行緒,相當於建立了一個匿名內部執行緒。
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> { try { Thread.sleep(2000); System.out.println("Thread run"); } catch (InterruptedException e) { e.printStackTrace(); } }); executorService.shutdownNow(); System.out.println("Main run"); }
Main run java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at ExecutorInterruptExample.lambda$main$0(ExecutorInterruptExample.java:9) at ExecutorInterruptExample$$Lambda$1/1160460865.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)
如果只想中斷 Executor 中的一個執行緒,可以通過使用 submit() 方法來提交一個執行緒,它會返回一個 Future<?> 物件,通過呼叫該物件的 cancel(true) 方法就可以中斷執行緒。
Future<?> future = executorService.submit(() -> { // .. }); future.cancel(true);