總結
java線程之間是協同式,不是搶占式
因為是協同式,所以線程之間都是“商量”着來,最佳實踐是沒有誰強迫誰終止的情況的,因此才會淘汰stop()方法,詳見 多線程 - 為何要棄用stop() suspend()?(不過你硬要調用stop()就另當別論...)
- public void interrupt() 會為指定線程設置一個interrupted狀態 -- 和stop()不同,不會強制線程停止,只會設置一個“interrupt標志位”,置為true(連續調用還是true,不會有異常發生。對比 Thread.start(), 多次調用會出現java.lang.IllegalStateException)。代表着“該線程被建議終止”。但是是否理會該建議,完全看該線程的具體邏輯是否去檢查這個標志位,然后根據標志做出不同的反應。也有可能該線程的業務邏輯完全不理會標志位的變化,也就會繼續我行我素的運行。
- public static boolean interrupted() 會判斷當前線程的interrupted狀態,並且會重置該狀態 -- 檢查是否當前線程被設置了“interrupt標志位”。如果被設置了,返回true,反之返回false。之后,該標志位會被清除。
- public boolean isInterrupted() 會判斷指定線程的interrupted狀態 -- 檢查是否當前線程被設置了“interrupt標志位”。如果被設置了,返回true,反之返回false。但是,該函數不會清除標志位。
@Test
public void testInterrupted(){
boolean b1 = Thread.interrupted(); //false
Thread.currentThread().interrupt();
boolean b2 = Thread.interrupted(); //true
boolean b3 = Thread.interrupted(); //false
}
@Test
public void testIsInterrupted(){
boolean b4 = Thread.currentThread().isInterrupted(); //false
Thread.currentThread().interrupt();
boolean b5 = Thread.currentThread().isInterrupted(); //true
boolean b6 = Thread.currentThread().isInterrupted(); //true
}
調用interrupt()拋出異常的情況(必看)
對於interrupt()方法,根據官方文檔:
- 如果指定線程調用了interrupt(),並且該線程不處於被sleep,wait,join的狀態,那么一切正常,不會拋出調用InterruptException異常,就不會清除中斷標志位。如果此時再調用isInterrupted()就會返回true。
- 如果指定線程調用了interrupt()時,而指定線程正處於阻塞狀態sleep,wait,join...線程自己就會立即拋InterruptException異常。
- 原本“interrupt標志位”應該是true(因為之前調用了interrupt()),也會被清除變為false。如果再調用isInterrupted()返回false。
- 如果你的程序不捕獲這個異常,線程就會異常終止,進入TERMINATED狀態;如果你的程序捕獲了這個異常,那么程序就會繼續執行catch語句塊(可能還有finally語句塊)以及以后的代碼。
- 需要注意的是,InterruptedException是線程自己從內部拋出的,並不是interrupt()方法拋出的。對某一線程調用interrupt()時,如果該線程正在執行普通的代碼,那么該線程根本就不會拋出InterruptedException。但是,一旦該線程進入到wait()/sleep()/join()后,就會立刻拋出InterruptedException。
Q:為什么當指定線程在被阻塞狀態(sleep, wait)時,調用interrupt(),要立即拋出InterruptException異常,並清除“interrupt標志位”(從true變為false)
A:【本答案不是很嚴謹,請大家斧正】線程在被阻塞過程中,可能掌握着某種資源。如果線程剛從阻塞回來,“interrupt標志位”就是true,很可能(視用戶自定義的代碼而定)線程就真准備中斷,沒有給程序任何回收資源的時間。因此先取消標志位,回收下資源然后視情況再決定是否真的interrupt。
如果中斷sleep,wait,join等,就會拋InterruptException異常,就會清除中斷標志位,那么這種情況應該怎么處理呢?為了保證數據的一致性和完整性,我們需要用Thread.interrupt()方法再次中斷自己,置上中斷標志位。例子如下:
@Test
public void testInterrupt() throws InterruptedException {
Thread t1 = new Thread() {
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interruted!");
break;
}
try {
Thread.sleep(2000); // 睡眠時中斷會清除中斷標志位
} catch (InterruptedException e) {
// 如果少了下面這句,這個線程雖然在外面中斷,但是只要中斷睡眠中的進程
// 就會清除中斷標志位,仍然處於無限循環,會競爭CPU資源
Thread.currentThread().interrupt(); // 再次中斷置上中斷標記
}
Thread.yield();
}
}
};
t1.start(); //t1 thread would running
Thread.sleep(200);//sleep main thread
t1.interrupt(); //after main thread wake up, would plan to interrupt t1 thread
}
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 - 如果當前線程無法修改該線程
