1. 程式人生 > >對Thread.interrupt()方法很詳細的介紹

對Thread.interrupt()方法很詳細的介紹

在JDK1.0中,可以用stop方法來終止,但是現在這種方法已經被禁用了,改用interrupt方法。

Thread.interrupt()方法不會中斷一個正在執行的執行緒。它的作用是,線上程受到阻塞時丟擲一箇中斷訊號,這樣執行緒就得以退出阻塞的狀態。更確切的說,如果執行緒被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,那麼,它將接收到一箇中斷異常(InterruptedException),從而提早地終結被阻塞狀態。

interrupt方法並不是強制終止執行緒,它只能設定執行緒的interrupted狀態,而線上程中一般使用一下方式:
while (!Thread.currentThread().isInterrupted() && more work to do)

{...}

而被block的執行緒(sleep() or join())在被呼叫interrupt時會產生InterruptException,此時是否終止執行緒由本執行緒自己決定。程式的一般形式是:
public void run()
{
try
{
. . .
while (!Thread.currentThread().isInterrupted() && more work to do)
{
do more work
}
}
catch(InterruptedException e)
{
// thread was interrupted during sleep or wait
}
finally
{
cleanup, if required
}
// exiting the run method terminates the thread
}

Thread.sleep方法也會產生InterruptedException,因此,如果每次在做完一些工作後呼叫了sleep方法,那麼就不用檢查isInterrupted,而是直接捕捉InterruptedException。

---------------------------------------------------------------------------------------

假如我們有一個任務如下,交給一個Java執行緒來執行,如何才能保證呼叫interrupt()來中斷它呢?

class ATask implements Runnable{  
  
    private double d = 0.0;  
      
    public void run() {  
        //死迴圈執行列印"I am running!" 和做消耗時間的浮點計算  
        while (true) {  
            System.out.println("I am running!");  
              
            for (int i = 0; i < 900000; i++) {  
                d =  d + (Math.PI + Math.E) / d;  
            }  
            //給執行緒排程器可以切換到其它程序的訊號  
            Thread.yield();  
        }  
    }  
}  
  
public class InterruptTaskTest {  
      
    public static void main(String[] args) throws Exception{  
        //將任務交給一個執行緒執行  
        Thread t = new Thread(new ATask());  
        t.start();  
          
        //執行一斷時間中斷執行緒  
        Thread.sleep(100);  
        System.out.println("****************************");  
        System.out.println("Interrupted Thread!");  
        System.out.println("****************************");  
        t.interrupt();  
    }  
}   


執行這個程式,我們發現呼叫interrupt()後,程式仍在執行,如果不強制結束,程式將一直執行下去,如下所示: 
......  
I am running!  
I am running!  
I am running!  
I am running!  
****************************  
Interrupted Thread!  
****************************  
I am running!  
I am running!  
I am running!  
I am running!  
I am running!  
....  

雖然中斷髮生了,但執行緒仍然在進行,離開執行緒有兩種常用的方法: 
丟擲InterruptedException和用Thread.interrupted()檢查是否發生中斷,下面分別看一下這兩種方法: 
1.在阻塞操作時如Thread.sleep()時被中斷會丟擲InterruptedException(注意,進行不能中斷的IO操作而阻塞和要獲得物件的鎖呼叫物件的synchronized方法而阻塞時不會丟擲InterruptedException) 
Java程式碼 
class ATask implements Runnable{  
  
    private double d = 0.0;  
      
    public void run() {  
        //死迴圈執行列印"I am running!" 和做消耗時間的浮點計算  
        try {  
            while (true) {  
                System.out.println("I am running!");  
                  
                for (int i = 0; i < 900000; i++) {  
                    d =  d + (Math.PI + Math.E) / d;  
                }  
                //休眠一斷時間,中斷時會丟擲InterruptedException  
                Thread.sleep(50);  
            }  
        } catch (InterruptedException e) {  
            System.out.println("ATask.run() interrupted!");  
        }  
    }  
}  

程式執行結果如下: 
Java程式碼 
I am running!  
I am running!  
****************************  
Interrupted Thread!  
****************************  
ATask.run() interrupted!  

可以看到中斷任務時讓任務丟擲InterruptedException來離開任務. 

2.Thread.interrupted()檢查是否發生中斷.Thread.interrupted()能告訴你執行緒是否發生中斷,並將清除中斷狀態標記,所以程式不會兩次通知你執行緒發生了中斷. 
Java程式碼 
class ATask implements Runnable{  
  
    private double d = 0.0;  
      
    public void run() {  
          
        //檢查程式是否發生中斷  
        while (!Thread.interrupted()) {  
            System.out.println("I am running!");  
  
            for (int i = 0; i < 900000; i++) {  
                d = d + (Math.PI + Math.E) / d;  
            }  
        }  
  
        System.out.println("ATask.run() interrupted!");  
    }  
}  

程式執行結果如下: 
Java程式碼 
I am running!  
I am running!  
I am running!  
I am running!  
I am running!  
I am running!  
I am running!  
****************************  
Interrupted Thread!  
****************************  
ATask.run() interrupted!  


我們可結合使用兩種方法來達到可以通過interrupt()中斷執行緒.請看下面例子: 
Java程式碼 
class ATask implements Runnable{  
  
    private double d = 0.0;  
      
    public void run() {  
          
        try {  
        //檢查程式是否發生中斷  
        while (!Thread.interrupted()) {  
            System.out.println("I am running!");  
            //point1 before sleep  
            Thread.sleep(20);  
            //point2 after sleep  
            System.out.println("Calculating");  
            for (int i = 0; i < 900000; i++) {  
                d = d + (Math.PI + Math.E) / d;  
            }  
        }  
          
        } catch (InterruptedException e) {  
            System.out.println("Exiting by Exception");  
        }  
          
        System.out.println("ATask.run() interrupted!");  
    }  
}  

在point1之前處point2之後發生中斷會產生兩種不同的結果,可以通過修改InterruptTaskTest main()裡的Thread.sleep()的時間來達到在point1之前產生中斷或在point2之後產生中斷. 
如果在point1之前發生中斷,程式會在呼叫Thread.sleep()時丟擲InterruptedException從而結束執行緒.這和在Thread.sleep()時被中斷是一樣的效果.程式執行結果可能如下:
Java程式碼 
I am running!  
Calculating  
I am running!  
Calculating  
I am running!  
Calculating  
I am running!  
****************************  
Interrupted Thread!  
****************************  
Exiting by Exception  
ATask.run() interrupted!  

如果在point2之後發生中斷,執行緒會繼續執行到下一次while判斷中斷狀態時.程式執行結果可能如下: 
Java程式碼 
I am running!  
Calculating  
I am running!  
Calculating  
I am running!  
Calculating  
****************************  
Interrupted Thread!  
****************************  
ATask.run() interrupted!