棟哥帶你學Java執行緒中斷
阿新 • • 發佈:2019-02-08
執行緒中斷
(一)stop方法
在早期,執行緒中又一個stop方法,可以直接終止執行緒.但是現在已經不用了,而且api上也已經在方法下標明已過時.這時因為stop方法一旦呼叫,不管執行緒是什麼狀態的都直接會被終
止,假如正在執行執行緒,run方法中的程式碼執行到一半被中斷了,那麼如果是像銀行這些地
方,會造成不可預計的事情發生,所以是很危險的.
(二)interrupt方法
既然stop方法不能使用,那麼我就去api看看還有沒有別的方法,然後api中有這個方法
interrupt()中斷執行緒。那麼我們來測試一下.
我們先寫一個類實現Runnable介面,然後重寫run方法,在run方法裡寫個死迴圈列印任意
內容,再使用interrupt看看能不能中斷.
public class Test {
public static void main(String[] args) {
TestRunnable runnable = new TestRunnable();
Thread t = new Thread(runnable);
t.start();
t.interrupt();
}
}
class TestRunnable implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("...");
}
}
}
結果:一直在迴圈列印...
那麼這時候就該想想是不是自己的程式碼有問題.然後我又去api找方法,這時候很巧,看到了
isInterrupted方法,它是用來判斷執行緒是否已經中斷.
接著我們在main函式里加上這幾句.
boolean interrupted = t.isInterrupted();
System.out.println(interrupted);
if (interrupted == false) {
t.interrupt();
}
isInterrupted()是個布林型別的方法,執行緒如果是執行狀態的話,我們輸出一下結果,
發現返回的是false,返回false的時候說明該執行緒沒有中斷.那我們就加個if 判斷,如果
返回false就是執行緒還沒中斷,我們就呼叫interrupt()方法中斷執行緒,再次執行.
執行結果:發現還是死迴圈列印.
這時候不管你是什麼心態,既然api上有那就是這個方法是可以用的,只是我們的測試方式
沒到位,我們再去看文件.點進去interrupt()方法.
public void interrupt()
中斷執行緒。
如果當前執行緒沒有中斷它自己(這在任何情況下都是允許的),則該執行緒的 checkAccess 方法就會被呼叫,這可能丟擲 SecurityException。
如果執行緒在呼叫 Object 類的 wait()、wait(long) 或 wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法過程中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。
如果該執行緒在可中斷的通道上的 I/O 操作中受阻,則該通道將被關閉,該執行緒的中斷狀態將被設定並且該執行緒將收到一個 ClosedByInterruptException。
如果該執行緒在一個 Selector 中受阻,則該執行緒的中斷狀態將被設定,它將立即從選擇操作返回,並可能帶有一個非零值,就好像呼叫了選擇器的 wakeup 方法一樣。
如果以前的條件都沒有儲存,則該執行緒的中斷狀態將被設定。
中斷一個不處於活動狀態的執行緒不需要任何作用。
丟擲:SecurityException - 如果當前執行緒無法修改該執行緒
文件裡說了這麼一大堆,我們挑重點來看.執行緒在呼叫Object類的wait、join、sleep方
法後再去呼叫interrupt方法,那麼就會清除它們的中斷狀態,並且報一箇中斷異常----
InterruptedException.
這時候把程式碼改成我現在這個樣子⬇️ :
public class Test {
public static void main(String[] args) {
TestRunnable runnable = new TestRunnable();
Thread t = new Thread(runnable);
t.start();
boolean interrupted = t.isInterrupted();
System.out.println(interrupted);
if (interrupted == false) {
t.interrupt();
boolean interrupted2 = t.isInterrupted();
System.out.println(interrupted2);
}
}
}
class TestRunnable implements Runnable {
private int test = 10;
@Override
public void run() {
while (test != 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("...");
test--;
}
}
}
輸出結果:
false
true
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at coam.lanou3g.TestRunnable.run(Test.java:25)
at java.lang.Thread.run(Thread.java:748)
...
...
...
...
...
...
...
...
...
...
解析:根據文件的說法,我在在使用了sleep方法後確實報了中斷異常,並且異常的右邊明
確指出是sleep interrupted,然後我們再看main函式裡,我線上程啟動後呼叫了一次
isInterrupted方法,然後再if裡面呼叫了interrupt中斷方法後,再次呼叫isInterrupted
方法,然後發現變成了ture.那麼我們就可以確定,呼叫interrupt方法清除狀態就是改變
isInterrupted方法的布林值.既然有了這個結論我們就再改一次程式碼.
public static void main(String[] args) {
TestRunnable runnable = new TestRunnable();
Thread t = new Thread(runnable);
t.start();
System.out.println("執行緒啟動啦");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.interrupt();
System.out.println("執行緒關閉啦");
}
class TestRunnable implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("...");
}
}
}
輸出結果:
執行緒啟動啦
...
執行緒關閉啦
然後我們就會發現這次是真的被我們關閉啦.
解析:因為一開始執行緒啟動的時候,isInterrupted返回的是false,但是while只有為
true的時候才執行while裡面的程式碼,所以我們在main函式裡把中斷方法interrupt加
上就可以改變它的值,while(false)不執行執行緒就結束.
結論:實際上這個interrupt()方法是用來設定isInterrupted方法的返回值.如果執行緒
中有wait()、sleep()、join()等方法,這時呼叫interrupt方法就會丟擲一個異常
InterruptedException,並且清除中斷狀態,如果執行緒中沒有出現這幾個方法,那麼這
時呼叫interrupt方法,就直接設定中斷狀態(true/false的改變).
(三)使用標記法
先上程式碼再解析.
public class Test {
public static void main(String[] args) {
TestRunnable runnable = new TestRunnable();
Thread t = new Thread(runnable);
t.start();
System.out.println("執行緒啟動啦");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
runnable.isOver = false;
System.out.println("執行緒關閉啦");
}
}
class TestRunnable implements Runnable {
public boolean isOver = true;
@Override
public void run() {
while (isOver) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("...");
}
}
}
執行結果:
執行緒啟動啦
...
執行緒關閉啦
...
解析:標記法中斷方法有點類似,都是靠一個值的改變來終止執行緒.這段程式碼裡,測試類裡面
定義了成員變數isOver並設定初始值為true,是為了執行while裡面的程式碼.然後在run
方法裡面讓執行緒沉睡一秒鐘,主要是不睡眠打印出來太多東西了,然後在main函式裡,先開
啟執行緒,然後列印執行緒啟動(方便觀看效果),繼續讓執行緒沉睡一秒,然後把TestRunnable
類裡的成員變數isOver設為false,類裡用public修飾是方便直接呼叫就不設為私有了,
測試的時候不用那麼麻煩,然後我們再看效果.可以能有些人會不一樣,第二行的...沒有,
這是正常的,這要看是執行緒進入run方法後先結束睡眠還是main函式裡先修改類的成員有
關.
interrupt方法和標記法的使用場合
一般我們首選標記法來終止執行緒,interrupt方法能不使用就儘量不要使用,如果要停止執行緒,直接使用標記法.但是如果遇到了等待狀態時,可以使用interrupt方法,強行清楚該狀態interrupt方法.