jinfeng_wang

          G-G-S,D-D-U!

          BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
            400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks
          程序是很簡(jiǎn)易的。然而,在編程人員面前,多線(xiàn)程呈現(xiàn)出了一組新的難題,如果沒(méi)有被恰當(dāng)?shù)慕鉀Q,將導(dǎo)致意外的行為以及細(xì)微的、難以發(fā)現(xiàn)的錯(cuò)誤。
                在本篇文章中,我們針對(duì)這些難題之一:如何中斷一個(gè)正在運(yùn)行的線(xiàn)程。 
                                                                                              
          背景
              中斷(Interrupt)一個(gè)線(xiàn)程意味著在該線(xiàn)程完成任務(wù)之前停止其正在進(jìn)行的一切,有效地中止其當(dāng)前的操作。線(xiàn)程是死亡、還是等待新的任務(wù)或是繼續(xù)運(yùn)行至下一步,就取決于這個(gè)程序。雖然初次看來(lái)它可能顯得簡(jiǎn)單,但是,你必須進(jìn)行一些預(yù)警以實(shí)現(xiàn)期望的結(jié)果。你最好還是牢記以下的幾點(diǎn)告誡。

              首先,忘掉Thread.stop方法。雖然它確實(shí)停止了一個(gè)正在運(yùn)行的線(xiàn)程,然而,這種方法是不安全也是不受提倡的,這意味著,在未來(lái)的JAVA版本中,它將不復(fù)存在。

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

          class Example1 extends Thread {
                      boolean stop=false;
                      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);
                      }
                      public void run() {
                      while(!stop){
                      System.out.println( "Thread is running..." );
                      long time = System.currentTimeMillis();
                      while((System.currentTimeMillis()-time < 1000)) {
                      }
                      }
                      System.out.println("Thread exiting under request..." );
                      }
                      }

          如果你運(yùn)行了Listing A中的代碼,你將在控制臺(tái)看到以下輸出:

          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 is running...

          Thread is running...

          Thread is running...
          ...............................
          甚至,在Thread.interrupt()被調(diào)用后,線(xiàn)程仍然繼續(xù)運(yùn)行。

          真正地中斷一個(gè)線(xiàn)程

              中斷線(xiàn)程最好的,最受推薦的方式是,使用共享變量(shared variable)發(fā)出信號(hào),告訴線(xiàn)程必須停止正在運(yùn)行的任務(wù)。線(xiàn)程必須周期性的核查這一變量(尤其在冗余操作期間),然后有秩序地中止任務(wù)。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..." );

            }

          }
           
          運(yùn)行Listing B中的代碼將產(chǎn)生如下輸出(注意線(xiàn)程是如何有秩序的退出的)

          Starting thread...

          Thread is running...

          Thread is running...

          Thread is running...

          Asking thread to stop...

          Thread exiting under request...

          Stopping application...

             雖然該方法要求一些編碼,但并不難實(shí)現(xiàn)。同時(shí),它給予線(xiàn)程機(jī)會(huì)進(jìn)行必要的清理工作,這在任何一個(gè)多線(xiàn)程應(yīng)用程序中都是絕對(duì)需要的。請(qǐng)確認(rèn)將共享變量定義成volatile 類(lèi)型或?qū)?duì)它的一切訪問(wèn)封入同步的塊/方法(synchronized blocks/methods)中。

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

          他們都可能永久的阻塞線(xiàn)程。即使發(fā)生超時(shí),在超時(shí)期滿(mǎn)之前持續(xù)等待也是不可行和不適當(dāng)?shù)模?,要使用某種機(jī)制使得線(xiàn)程更早地退出被阻塞的狀態(tài)。

          很不幸運(yùn),不存在這樣一種機(jī)制對(duì)所有的情況都適用,但是,根據(jù)情況不同卻可以使用特定的技術(shù)。在下面的環(huán)節(jié),我將解答一下最普遍的例子。

          使用Thread.interrupt()中斷線(xiàn)程


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

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

          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;//如果線(xiàn)程阻塞,將不會(huì)檢查此變量

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

          Starting thread...

          Thread running...

          Thread running...

          Thread running...

          Asking thread to stop...

          Thread interrupted...

          Thread exiting under request...

          Stopping application...


          中斷I/O操作
              然而,如果線(xiàn)程在I/O操作進(jìn)行時(shí)被阻塞,又會(huì)如何?I/O操作可以阻塞線(xiàn)程一段相當(dāng)長(zhǎng)的時(shí)間,特別是牽扯到網(wǎng)絡(luò)應(yīng)用時(shí)。例如,服務(wù)器可能需要等待一個(gè)請(qǐng)求(request),又或者,一個(gè)網(wǎng)絡(luò)應(yīng)用程序可能要等待遠(yuǎn)端主機(jī)的響應(yīng)。

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

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

          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..." );

                }

              }

            }

          }


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

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

          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..." );
            }
          }
          以下是運(yùn)行Listing E中代碼后的輸出:

          Starting thread...

          Waiting for connection...

          Asking thread to stop...

          accept() failed or interrupted...

          Thread exiting under request...

          Stopping application...

          多線(xiàn)程是一個(gè)強(qiáng)大的工具,然而它正呈現(xiàn)出一系列難題。其中之一是如何中斷一個(gè)正在運(yùn)行的線(xiàn)程。如果恰當(dāng)?shù)貙?shí)現(xiàn),使用上述技術(shù)中斷線(xiàn)程將比使用Java平臺(tái)上已經(jīng)提供的內(nèi)嵌操作更為簡(jiǎn)單。

          ============================================

          Writing multithreaded programs in Java, with its built-in support for threads, is fairly straightforward. However, multithreading presents a whole set of new challenges to the programmer that, if not correctly addressed, can lead to unexpected behavior and subtle, hard-to-find errors. In this article, we address one of those challenges: how to interrupt a running thread.

          Background
          Interrupting a thread means stopping what it is doing before it has completed its task, effectively aborting its current operation. Whether the thread dies, waits for new tasks, or goes on to the next step depends on the application.

          Although it may seem simple at first, you must take some precautions in order to achieve the desired result. There are some caveats you must be aware of as well.

          First of all, forget the Thread.stop method. Although it indeed stops a running thread, the method is unsafe and was deprecated, which means it may not be available in future versions of the Java.

          Another method that can be confusing for the unadvised is Thread.interrupt. Despite what its name may imply, the method does not interrupt a running thread (more on this later), as Listing A demonstrates. It creates a thread and tries to stop it using Thread.interrupt. The calls to Thread.sleep() give plenty of time for the thread initialization and termination. The thread itself does not do anything useful.

          If you run the code in Listing A, you should see something like this on your console:
          Starting thread...
          Thread is running...
          Thread is running...
          Thread is running...
          Interrupting thread...
          Thread is running...
          Thread is running...
          Thread is running...
          Stopping application...

          Even after Thread.interrupt() is called, the thread continues to run for a while.

          Really interrupting a thread
          The best, recommended way to interrupt a thread is to use a shared variable to signal that it must stop what it is doing. The thread must check the variable periodically, especially during lengthy operations, and terminate its task in an orderly manner. Listing B demonstrates this technique.

          Running the code in Listing B will generate output like this (notice how the thread exits in an orderly fashion):
          Starting thread...
          Thread is running...
          Thread is running...
          Thread is running...
          Asking thread to stop...
          Thread exiting under request...
          Stopping application...

          Although this method requires some coding, it is not difficult to implement and give the thread the opportunity to do any cleanup needed, which is an absolute requirement for any multithreaded application. Just be sure to declare the shared variable as volatile or enclose any access to it into synchronized blocks/methods.

          So far, so good! But what happens if the thread is blocked waiting for some event? Of course, if the thread is blocked, it can't check the shared variable and can't stop. There are plenty of situations when that may occur, such as calling Object.wait(), ServerSocket.accept(), and DatagramSocket.receive(), to name a few.

          They all can block the thread forever. Even if a timeout is employed, it may not be feasible or desirable to wait until the timeout expires, so a mechanism to prematurely exit the blocked state must be used.

          Unfortunately there is no such mechanism that works for all cases, but the particular technique to use depends on each situation. In the following sections, I'll give solutions for the most common cases.

          Interrupting a thread with Thread.interrupt()
          As demonstrated in Listing A, the method Thread.interrupt() does not interrupt a running thread. What the method actually does is to throw an interrupt if the thread is blocked, so that it exits the blocked state. More precisely, if the thread is blocked at one of the methods Object.wait, Thread.join, or Thread.sleep, it receives an InterruptedException, thus terminating the blocking method prematurely.

          So, if a thread blocks in one of the aforementioned methods, the correct way to stop it is to set the shared variable and then call the interrupt() method on it (notice that it is important to set the variable first). If the thread is not blocked, calling interrupt() will not hurt; otherwise, the thread will get an exception (the thread must be prepared to handle this condition) and escape the blocked state. In either case, eventually the thread will test the shared variable and stop. Listing C is a simple example that demonstrates this technique.

          As soon as Thread.interrupt() is called in Listing C, the thread gets an exception so that it escapes the blocked state and determines that it should stop. Running this code produces output like this:
          Starting thread...
          Thread running...
          Thread running...
          Thread running...
          Asking thread to stop...
          Thread interrupted...
          Thread exiting under request...
          Stopping application...

          Interrupting an I/O operation
          But what happens if the thread is blocked on an I/O operation? I/O can block a thread for a considerable amount of time, particularly if network communication is involved. For example, a server may be waiting for a request, or a network application may be waiting for an answer from a remote host.

          If you're using channels, available with the new I/O API introduced in Java 1.4, the blocked thread will get a ClosedByInterruptException exception. If that is the case, the logic is the same as that used in the third example—only the exception is different.

          But you might be using the traditional I/O available since Java 1.0, since the new I/O is so recent and requires more work. In this case, Thread.interrupt() doesn't help, since the thread will not exit the blocked state. Listing D demonstrates that behavior. Although the interrupt() method is called, the thread does not exit the blocked state.

          Fortunately, the Java Platform provides a solution for that case by calling the close() method of the socket the thread is blocked in. In this case, if the thread is blocked in an I/O operation, the thread will get a SocketException exception, much like the interrupt() method causes an InterruptedException to be thrown.

          The only caveat is that a reference to the socket must be available so that its close() method can be called. That means the socket object must also be shared. Listing E demonstrates this case. The logic is the same as in the examples presented so far.

          And here's the sample output you can expect from running Listing E:
          Starting thread...
          Waiting for connection...
          Asking thread to stop...
          accept() failed or interrupted...
          Thread exiting under request...
          Stopping application...

          Multithreading is a powerful tool, but it presents its own set of challenges. One of these is how to interrupt a running thread. If properly implemented, these techniques make interrupting a thread no more difficult than using the built-in operations already provided by the Java Platform.
          posted on 2008-04-27 16:16 jinfeng_wang 閱讀(38632) 評(píng)論(12)  編輯  收藏 所屬分類(lèi): java 、ZZ

          評(píng)論

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz)[未登錄](méi) 2008-11-26 21:48 客人
          寫(xiě)著篇文章的人似乎對(duì)Java線(xiàn)程理解得不透哦……  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz) 2010-12-21 17:10 lll
          很詳細(xì)頂了  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz)[未登錄](méi) 2011-08-31 09:29 lz
          @客人
          我覺(jué)的很厲害了,咋還不夠透,要不你講透徹一點(diǎn),我最近就是要中斷一個(gè)等待另外一個(gè)系統(tǒng),返回值的線(xiàn)程,可能網(wǎng)絡(luò)丟失,那個(gè)線(xiàn)程卡在那了,現(xiàn)在要關(guān)閉當(dāng)前的,重新啟動(dòng)一個(gè)該線(xiàn)程。  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz) 2011-10-25 12:55 誰(shuí)不可以
          public class TestThread extends Thread {


          public void run() {
          while(!Thread.currentThread().isInterrupted()){
          System.out.println("------------before interrupt");
          this.interrupt();
          System.out.println("-------end interupt");

          }
          System.out.println("-----------------end-----------------");
          }

          }  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz) 2011-10-25 12:56 誰(shuí)不可以
          @誰(shuí)不可以
          public class ThreadMain {

          public static void main(String[] args) {
          Thread test = new TestThread();
          test.start();
          }

          }
            回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz) 2012-04-22 16:49 紅淚
          說(shuō)了一些基本的東西哦..呵呵.不過(guò)也不錯(cuò)  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz) 2013-11-23 11:45 toney
          好文章必須贊,根據(jù)您的辦法果然解決了一個(gè)大問(wèn)題??!  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz) 2014-04-07 14:15 graykeel
          雖然不想說(shuō),但是還是忍不住,首先你要明確interrupt方法的意思,它是設(shè)置一個(gè)標(biāo)志來(lái)告訴線(xiàn)程已中斷,其次它會(huì)使得正在進(jìn)行sleep、wait和join的方法拋出InterruptException異常,你的Example1中只是設(shè)置了標(biāo)志,而沒(méi)有檢查標(biāo)志,或者滿(mǎn)足拋出異常的條件,而且在你進(jìn)行了中斷標(biāo)志設(shè)置之后并沒(méi)有去檢查該標(biāo)志,線(xiàn)程當(dāng)然會(huì)自動(dòng)的執(zhí)行下去
          修改為:public void run() {
          while(!stop){
          if (Thread.currentThread().isInterrupted())
          return;
          System.out.println( "Thread is running..." );
          long time = System.currentTimeMillis();
          while((System.currentTimeMillis()-time < 1000)) {
          }
          try {
          wait(100);
          } catch (InterruptedException e) {
          e.printStackTrace();
          }
          }
          System.out.println("Thread exiting under request..." );
          }
            回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz) 2014-08-20 14:11 question
          @graykeel
          你看了沒(méi)有?還是理解能力太差了啊。人家那些例子是不斷講解循序漸進(jìn)的,包括第一個(gè)例子為什么不能達(dá)到期望的分析。你跑過(guò)來(lái)說(shuō)人家第一個(gè)例子沒(méi)有怎么么的,人家也沒(méi)說(shuō)那是正確性為,是反例好不好。  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz) 2014-09-28 16:01 劉三
          樓主舉的例子循序漸進(jìn)的,講得很明白  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz)[未登錄](méi) 2014-11-25 10:56 編程浪子
          實(shí)際上樓主沒(méi)有樓主想想的那么復(fù)雜,如果結(jié)束一個(gè)線(xiàn)程只需要調(diào)用線(xiàn)程的interrupt方法,然后在線(xiàn)程的‘死循環(huán)’中設(shè)置線(xiàn)程的睡眠時(shí)間,在設(shè)置‘睡眠’時(shí)間中撲捉InterruptedException異常,如果撲捉到異常則直接break循環(huán)體,線(xiàn)程自然就結(jié)束掉了!  回復(fù)  更多評(píng)論
            

          # re: Java Thread.interrupt 害人! 中斷JAVA線(xiàn)程(zz)[未登錄](méi) 2014-12-26 21:17 碼魂
          @編程浪子
          哈哈,你想的太簡(jiǎn)單了...你睡眠的時(shí)候interrupt不一定剛好命中你.所以你還是永遠(yuǎn)結(jié)束不了.  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 屏山县| 丰都县| 安溪县| 长武县| 黑河市| 昂仁县| 靖边县| 阳原县| 家居| 枣庄市| 澄城县| 札达县| 鹤山市| 连江县| 望奎县| 察雅县| 剑阁县| 琼中| 英德市| 高清| 黎川县| 英吉沙县| 仁化县| 吉安县| 合作市| 阿克| 龙岩市| 德保县| 巴林右旗| 边坝县| 乌鲁木齐市| 望谟县| 庄浪县| 华池县| 江达县| 万州区| 富裕县| 繁峙县| 景洪市| 普兰店市| 石狮市|