Java Thread.interrupt( )中斷線程


使用Thread.interrupt()中斷線程

正如Listing A中所描述的,Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,在線程受到阻塞時拋出一個中斷信號,這樣線程就得以退出阻塞的狀態。更確切的說,如果線程被Object.wait, Thread.join和 Thread.sleep三種方法之一阻塞,那么,它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態。

因此,如果線程被上述幾種方法阻塞,正確的停止線程方式是設置共享變量,並調用interrupt()(注意變量應該先設置)。如果線程沒有被阻塞,這時調用interrupt()將不起作用;否則,線程就將得到異常(該線程必須事先預備好處理此狀況),接着逃離阻塞狀態。在任何一種情況中,最后線程都將檢查共享變量然后再停止。Listing C這個示例描述了該技術。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Listing C
 
class Example3 extends Thread {
 
     volatile boolean stop = false ;
 
     public static void main(String args[]) throws Exception {
         Example3 thread = new Example3();
         System.out.println( "Starting thread..." );
         thread.start();
         Thread.sleep( 3000 );
         System.out.println( "Asking thread to stop..." );
         thread.stop = true ; //如果線程阻塞,將不會檢查此變量
         thread.interrupt();
         Thread.sleep( 3000 );
         System.out.println( "Stopping application..." );
         //System.exit( 0 );
     }
 
     public void run() {
         while (!stop) {
             System.out.println( "Thread running..." );
             try {
                 Thread.sleep( 1000 );
             } catch (InterruptedException e) {
                 System.out.println( "Thread interrupted..." );
             }
         }
         System.out.println( "Thread exiting under request..." );
     }
}

    一旦Listing C中的Thread.interrupt()被調用,線程便收到一個異常,於是逃離了阻塞狀態並確定應該停止。運行以上代碼將得到下面的輸出:

?
1
2
3
4
5
6
7
8
Starting thread...
Thread running...
Thread running...
Thread running...
Asking thread to stop...
Thread interrupted...
Thread exiting under request...
Stopping application...

中斷I/O操作

然而,如果線程在I/O操作進行時被阻塞,又會如何?I/O操作可以阻塞線程一段相當長的時間,特別是牽扯到網絡應用時。例如,服務器可能需要等待一個請求(request),又或者,一個網絡應用程序可能要等待遠端主機的響應。

如果你正使用通道(channels)(這是在Java 1.4中引入的新的I/O API),那么被阻塞的線程將收到一個 ClosedByInterruptException異常。如果情況是這樣,其代碼的邏輯和第三個例子中的是一樣的,只是異常不同而已。

但是,你可能正使用Java1.0之前就存在的傳統的I/O,而且要求更多的工作。既然這樣,Thread.interrupt()將不起作用,因為線程將不會退出被阻塞狀態。Listing D描述了這一行為。盡管interrupt()被調用,線程也不會退出被阻塞狀態。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Listing D
import java.io.*;
 
class Example4 extends Thread {
     public static void main( String args[] ) throws Exception {
         Example4 thread = new Example4();
         System.out.println( "Starting thread..." );
         thread.start();
         Thread.sleep( 3000 );
         System.out.println( "Interrupting thread..." );
         thread.interrupt();
         Thread.sleep( 3000 );
         System.out.println( "Stopping application..." );
         //System.exit( 0 );
     }
 
     public void run() {
         ServerSocket socket;
         try {
             socket = new ServerSocket( 7856 );
         } catch (IOException e) {
             System.out.println( "Could not create the socket..." );
             return ;
         }
         while ( true ) {
             System.out.println( "Waiting for connection..." );
             try {
                 Socket sock = socket.accept();
             } catch (IOException e) {
                 System.out.println( "accept() failed or interrupted..." );
             }
         }
     }
}

很幸運,Java平台為這種情形提供了一項解決方案,即調用阻塞該線程的套接字的close()方法。在這種情形下,如果線程被I/O操作阻塞,該線程將接收到一個SocketException異常,這與使用interrupt()方法引起一個InterruptedException異常被拋出非常相似。

唯一要說明的是,必須存在socket的引用(reference),只有這樣close()方法才能被調用。這意味着socket對象必須被共享。Listing E描述了這一情形。運行邏輯和以前的示例是相同的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Listing E
import java.net.*;
import java.io.*;
 
class Example5 extends Thread {
     volatile boolean stop = false ;
     volatile ServerSocket socket;
 
     public static void main(String args[]) throws Exception {
         Example5 thread = new Example5();
         System.out.println( "Starting thread..." );
         thread.start();
         Thread.sleep( 3000 );
         System.out.println( "Asking thread to stop..." );
         thread.stop = true ;
         thread.socket.close();
         Thread.sleep( 3000 );
         System.out.println( "Stopping application..." );
         //System.exit( 0 );
     }
 
     public void run() {
         try {
             socket = new ServerSocket( 7856 );
         } catch (IOException e) {
             System.out.println( "Could not create the socket..." );
             return ;
         }
         while (!stop) {
             System.out.println( "Waiting for connection..." );
             try {
                 Socket sock = socket.accept();
             } catch (IOException e) {
                 System.out.println( "accept() failed or interrupted..." );
             }
         }
         System.out.println( "Thread exiting under request..." );
     }
}

    以下是運行Listing E中代碼后的輸出:

?
1
2
3
4
5
6
Starting thread...
Waiting for connection...
Asking thread to stop...
accept() failed or interrupted...
Thread exiting under request...
Stopping application...

多線程是一個強大的工具,然而它正呈現出一系列難題。其中之一是如何中斷一個正在運行的線程。如果恰當地實現,使用上述技術中斷線程將比使用Java平台上已經提供的內嵌操作更為簡單。


[線程的interrupt()方法,interrupted()isInterrupted()] 的區別


  這三個方法是關系非常密切而且又比較復雜的,雖然它們各自的功能很清楚,但它們之間的關系有大多數 人不是真正的了解. 

  interrupt()方法,它是實例方法,而它也是最奇怪的方法,在java語言中,線程最初被設計為 "隱晦難懂 " 的東西,直到現在它的語義不沒有象它的名字那樣准確. 
     

  大多數人以為,一個線程象調用了interrupt()方法,那它對應的線程就應該被中斷而拋出異常, 事實中,當一個線程對象調用interrupt()方法,它對應的線程並沒有被中斷,只是改變了它的中斷狀態. 使當前線程的狀態變以中斷狀態,如果沒有其它影響,線程還會自己繼續執行. 只有當線程執行到sleep,wait,join等方法時,或者自己檢查中斷狀態而拋出異常的情況下,線程 才會拋出異常. 

  如果線程對象調用interrupt()后它對應的線程就立即中斷,那么interrupted()方法就不可能執行. 因為interrupted()方法是一個static方法,就是說只能在當前線程上調用,而如果一個線程interrupt()后它已經中斷了,那它又如何讓自己interrupted()? 
        

  正因為一個線程調用interrupt()后只是改變了中斷狀態,它可以繼續執行下去,在沒有調用sleep, wait,join等法或自己拋出異常之前,它就可以調用interrupted()來清除中斷狀態(還會原狀) 
interrupted()方法會檢查當前線程的中斷狀態,如果為 "被中斷狀態 "則改變當前線程為 "非中斷狀態 "並返回true,如果為 "非中斷狀態 "則返回false,它不僅檢查當前線程是否為中斷狀態,而且在保證當前線程回來非中斷狀態,所以它叫 "interrupted ",是說中斷的狀態已經結束(到非中斷狀態了) 

  isInterrupted()方法則僅僅檢查線程對象對應的線程是否是中斷狀態,並不改變它的狀態. 
        
  目前大家只能先記住這三個方法的功能,只有真正深入到多線程編程實踐中,才會體會到它們為什么 是對象方法,為什么是類方法.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM