筆者是廣州的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一起學習這部分的內容。技術的進步需要每個小小的積累,才能走得更遠,更長久。