FORTUNE

          THE WAY TO THE MASTER...
          posts - 49, comments - 18, trackbacks - 0, articles - 1
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          中斷JAVA線程

          Posted on 2006-04-12 13:36 fortune 閱讀(591) 評論(0)  編輯  收藏 所屬分類: java技術
          在JAVA中,通過其對線程類的內嵌支持,編程人員編寫多線程程序是很簡易的。然而,在編程人員面前,多線程呈現出了一組新的難題,如果沒有被恰當的解決,將導致意外的行為以及細微的、難以發現的錯誤。在本篇文章中,我們針對這些難題之一:如何中斷一個正在運行的線程。?

          背景
          中斷(Interrupt)一個線程意味著在該線程完成任務之前停止其正在進行的一切,有效地中止其當前的操作。線程是死亡、還是等待新的任務或是繼續運行至下一步,就取決于這個程序。

          雖然初次看來它可能顯得簡單,但是,你必須進行一些預警以實現期望的結果。你最好還是牢記以下的幾點告誡。

          首先,忘掉Thread.stop方法。雖然它確實停止了一個正在運行的線程,然而,這種方法是不安全也是不受提倡的,這意味著,在未來的JAVA版本中,它將不復存在。

          一些輕率的家伙可能被另一種方法Thread.interrupt所迷惑。盡管,其名稱似乎在暗示著什么,然而,這種方法并不會中斷一個正在運行的線程(待會將進一步說明),正如Listing?A中描述的那樣。它創建了一個線程,并且試圖使用Thread.interrupt方法停止該線程。Thread.sleep()方法的調用,為線程的初始化和中止提供了充裕的時間。線程本身并不參與任何有用的操作。

          Listing?A
          class?Example1?extends?Thread?{

          ??public?static?void?main(?String?args[]?)?throws?Exception?{

          ????Example1?thread?=?new?Example1();

          ???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?);

          ??}


          如果你運行了Listing?A中的代碼,你將在控制臺看到以下輸出:

          Starting?thread...

          Thread?is?running...

          Thread?is?running...

          Thread?is?running...

          Interrupting?thread...

          Thread?is?running...

          Thread?is?running...

          Thread?is?running...

          Stopping?application...

          甚至,在Thread.interrupt()被調用后,線程仍然繼續運行了一段時間。

          真正地中斷一個線程

          中斷線程最好的,最受推薦的方式是,使用共享變量(shared?variable)發出信號,告訴線程必須停止正在運行的任務。線程必須周期性的核查這一變量(尤其在冗余操作期間),然后有秩序地中止任務。Listing?B描述了這一方式。

          Listing?B
          class?Example2?extends?Thread?{

          ??volatile?boolean?stop?=?false;

          ??public?static?void?main(?String?args[]?)?throws?Exception?{

          ????Example2?thread?=?new?Example2();

          ???System.out.println(?"Starting?thread..."?);

          ???thread.start();

          ???Thread.sleep(?3000?);

          ???System.out.println(?"Asking?thread?to?stop..."?);

          ???thread.stop?=?true;

          ???Thread.sleep(?3000?);

          ???System.out.println(?"Stopping?application..."?);

          ???System.exit(?0?);

          ??}

          ??public?void?run()?{

          ????while?(?!stop?)?{

          ?????System.out.println(?"Thread?is?running..."?);

          ??????long?time?=?System.currentTimeMillis();

          ??????while?(?(System.currentTimeMillis()-time?<?1000)?&&?(!stop)?)?{

          ??????}

          ????}

          ???System.out.println(?"Thread?exiting?under?request..."?);

          ??}

          }

          ?


          運行Listing?B中的代碼將產生如下輸出(注意線程是如何有秩序的退出的)

          Starting?thread...

          Thread?is?running...

          Thread?is?running...

          Thread?is?running...

          Asking?thread?to?stop...

          Thread?exiting?under?request...

          Stopping?application...

          雖然該方法要求一些編碼,但并不難實現。同時,它給予線程機會進行必要的清理工作,這在任何一個多線程應用程序中都是絕對需要的。請確認將共享變量定義成volatile?類型或將對它的一切訪問封入同步的塊/方法(synchronized?blocks/methods)中。

          到目前為止一切順利!但是,當線程等待某些事件發生而被阻塞,又會發生什么?當然,如果線程被阻塞,它便不能核查共享變量,也就不能停止。這在許多情況下會發生,例如調用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()時,這里僅舉出一些。

          他們都可能永久的阻塞線程。即使發生超時,在超時期滿之前持續等待也是不可行和不適當的,所以,要使用某種機制使得線程更早地退出被阻塞的狀態。

          很不幸運,不存在這樣一種機制對所有的情況都適用,但是,根據情況不同卻可以使用特定的技術。在下面的環節,我將解答一下最普遍的例子。

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

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

          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()被調用,線程便收到一個異常,于是逃離了阻塞狀態并確定應該停止。運行以上代碼將得到下面的輸出:

          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,因為新的I/O是最近才引入,而且要求更多的工作。既然這樣,Thread.interrupt()將不起作用,因為線程將不會退出被阻塞狀態。Listing?D描述了這一行為。盡管interrupt()被調用,線程也不會退出被阻塞狀態

          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描述了這一情形。運行邏輯和以前的示例是相同的。

          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中代碼后的輸出:

          Starting?thread...

          Waiting?for?connection...

          Asking?thread?to?stop...

          accept()?failed?or?interrupted...

          Thread?exiting?under?request...

          Stopping?application...

          多線程是一個強大的工具,然而它正呈現出一系列難題。其中之一是如何中斷一個正在運行的線程。如果恰當地實現,使用上述技術中斷線程將比使用Java平臺上已經提供的內嵌操作更為簡單。
          主站蜘蛛池模板: 荥阳市| 精河县| 陆丰市| 中西区| 隆子县| 章丘市| 唐河县| 万山特区| 盐亭县| 涞水县| 许昌市| 安仁县| 隆回县| 郑州市| 永修县| 鹿泉市| 福海县| 贵港市| 厦门市| 和田县| 绥宁县| 丰原市| 南阳市| 图木舒克市| 佛冈县| 出国| 虞城县| 普定县| 桦甸市| 竹山县| 桑日县| 辽源市| 金山区| 饶平县| 东兰县| 枝江市| 利川市| 新沂市| 阿城市| 临江市| 久治县|