多執行緒併發篇——如何停止執行緒
筆者是廣州的java程式設計師,剛畢業半年,工作之餘寫部落格,如果覺得我的文章寫得不錯,可以關注我的微信公眾號(J2彬彬),裡面會有更多精彩內容。從2018年8月份開始寫部落格,希望日後寫出更多通俗易懂的技術文章與大家一同分享。
前言
你有沒有想過,如何停止一個執行緒?很多人首先會想到Thread.stop()方法,但是這個方法已經過時,不推薦使用,因為這個方法會帶來安全問題,什麼安全問題呢?後面會有詳細說明。我們先講講目前JDK API推薦使用停止執行緒的方法Thread.interrupt()方法。
Thread.interrupt()
既然不能直接stop執行緒,那麼只有一種方法可以讓執行緒結束,那就是讓run方法運結束。 Thread.interrupt()代表的意思是“停止,中止”。但是這個方法需要加入一個判斷才可以完成執行緒的停止。一旦檢測到執行緒處於中斷狀態,那麼就有機會結束run方法。 下面以一個程式碼示例看看它是如何停止執行緒的?
一、interrupt()停止執行緒
package com.bingo.thread.stopThread; /** * Created with IntelliJ IDEA. * Description: 停止執行緒不推薦使用stop方法,此方法不安全,我們可以使用Thread.interrupt() * User: bingo */ public class Run { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } myThread.interrupt(); System.out.println("end..."); } } class MyThread extends Thread{ @Override public void run() { super.run(); for (int i = 0; i < 10000 ; i++) { if(this.isInterrupted()){ System.out.println("已經是停止狀態了,我要退出了"); break; } System.out.println("i="+(i+1)); } } } 複製程式碼
執行結果:
i=1 ...... i=3102 i=3103 i=3104 i=3105 i=3106 i=3107 i=3108 end... 已經是停止狀態了,我要退出了 複製程式碼
執行結果我們可以看到,當myThread執行緒的迴圈執行到i=3108的時候,由於執行緒被中斷,而跳出迴圈,這個例子很好詮釋了interrupt方法的作用。
二、interrupt可以清除執行緒的凍結狀態,讓執行緒恢復到可執行的狀態上來
package com.bingo.thread.stopThread; /** * Created with IntelliJ IDEA. * Description: interrupt可以清除執行緒的凍結狀態,讓執行緒恢復到可執行的狀態上來。 * User: bingo */ public class Run2 { public static void main(String[] args) { MyThread2 thread = new MyThread2(); thread.start(); thread.interrupt(); System.out.println("main end..."); } } class MyThread2 extends Thread{ @Override public void run() { System.out.println("run begin..."); try { Thread.sleep(1000000); } catch (InterruptedException e) { System.out.println("run 在沉睡中被中止,進入catch"); e.printStackTrace(); } System.out.println("run end..."); } } 複製程式碼
執行結果:
main end... run begin... run 在沉睡中被中止,進入catch run end... java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.bingo.thread.stopThread.MyThread2.run(Run2.java:26) 複製程式碼
從執行結果我們可以看到,本來run方法睡眠時間為1000秒,但是列印結果卻是瞬間的,其實sleep已經被interrupt方法給打斷,此時執行緒凍結狀態被清除,並丟擲異常,被catch捕獲,列印異常資訊。
暴力停止——Stop
下圖是JDK API對stop方法的描述,可以看到已過時,不推薦使用,並告訴我們此方法為何不安全?

如果某個執行緒加鎖,stop方法停止該執行緒時會把鎖釋放掉,可能造成資料不一致的情況。下面程式碼可以說明此問題。
package com.bingo.thread.stopThread; /** * Created with IntelliJ IDEA. * Description: stop()方法為何不安全?下面例子可解答 * User: bingo */ public class StopTest { public static void main(String[] args) { try { SynchrionzedObject object = new SynchrionzedObject(); MyThread3 t = new MyThread3(object); t.start(); Thread.sleep(500); t.stop(); System.out.println(object.getUsername()+" "+object.getPassword()); } catch (InterruptedException e) { e.printStackTrace(); } } } class SynchrionzedObject{ private String username = "a"; private String password = "aa"; public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public String getPassword() { return password; } public synchronized void printString(String username,String password){ try { this.username = username; Thread.sleep(10000); this.password = password; } catch (InterruptedException e) { e.printStackTrace(); } } } class MyThread3 extends Thread{ private SynchrionzedObject object; public MyThread3(SynchrionzedObject object){ this.object = object; } @Override public void run() { object.printString("b", "bb"); } } 複製程式碼
執行結果:
b aa 複製程式碼
從上面例子我們可以看到雖然printString方法加了鎖,但是run方法執行過程中突然被stop了,鎖被釋放,MyThread執行緒只對username進行了賦值,而password賦值動作未執行,此時造成資料不一致。
最後
其實java多執行緒很多方法內部都是native方法,也就是基於JVM內部實現的,所以我們有必要結合JVM一起學習這部分的內容。技術的進步需要每個小小的積累,才能走得更遠,更長久。