`

停止Java线程,小心interrupt()方法

 
阅读更多

http://blog.csdn.net/wxwzy738/article/details/8516253

 

程序是很简易的。然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决,将导致意外的行为以及细微的、难以发现的错误。
  在本篇文章中,我们针对这些难题之一:如何中断一个正在运行的线程。 

背景 
    中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序。虽然初次看来它可能显得简单,但是,你必须进行一些预警以实现期望的结果。你最好还是牢记以下的几点告诫。 

    首先,忘掉Thread.stop方法。虽然它确实停止了一个正在运行的线程,然而,这种方法是不安全也是不受提倡的,这意味着,在未来的Java版本中,它将不复存在。 

    一些轻率的家伙可能被另一种方法Thread.interrupt所迷惑。尽管,其名称似乎在暗示着什么,然而,这种方法并不会中断一个正在运行的线程(待会将进一步说明),正如Listing A中描述的那样。它创建了一个线程,并且试图使用Thread.interrupt方法停止该线程。Thread.sleep()方法的调用,为线程的初始化和中止提供了充裕的时间。线程本身并不参与任何有用的操作。 
Listing A 

  1. class Example1 extends Thread {  
  2.             boolean stop=false;  
  3.             public static void main( String args[] ) throws Exception {  
  4.             Example1 thread = new Example1();  
  5.             System.out.println( "Starting thread..." );  
  6.             thread.start();  
  7.             Thread.sleep( 3000 );  
  8.             System.out.println( "Interrupting thread..." );  
  9.             thread.interrupt();  
  10.             Thread.sleep( 3000 );  
  11.             System.out.println("Stopping application..." );  
  12.             //System.exit(0);  
  13.             }  
  14.             public void run() {  
  15.             while(!stop){  
  16.             System.out.println( "Thread is running..." );  
  17.             long time = System.currentTimeMillis();  
  18.             while((System.currentTimeMillis()-time < 1000)) {  
  19.             }  
  20.             }  
  21.             System.out.println("Thread exiting under request..." );  
  22.             }  
  23.             }  

 

如果你运行了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 is running... 

Thread is running... 

Thread is running... 
............................... 
甚至,在Thread.interrupt()被调用后,线程仍然继续运行。 

真正地中断一个线程 

    中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务。Listing B描述了这一方式。 

Listing B 

 

 

  1. class Example2 extends Thread {  
  2.   volatile boolean stop = false;  
  3.   public static void main( String args[] ) throws Exception {  
  4.    Example2 thread = new Example2();  
  5.    System.out.println( "Starting thread..." );  
  6.    thread.start();  
  7.    Thread.sleep( 3000 );  
  8.    System.out.println( "Asking thread to stop..." );  
  9.   
  10.    thread.stop = true;  
  11.    Thread.sleep( 3000 );  
  12.    System.out.println( "Stopping application..." );  
  13.    //System.exit( 0 );  
  14.   }  
  15.   
  16.   public void run() {  
  17.     while ( !stop ) {  
  18.      System.out.println( "Thread is running..." );  
  19.       long time = System.currentTimeMillis();  
  20.       while ( (System.currentTimeMillis()-time < 1000) && (!stop) ) {  
  21.       }  
  22.     }  
  23.    System.out.println( "Thread exiting under request..." );  
  24.   }  
  25. }  

运行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 

 

 

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

一旦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,而且要求更多的工作。既然这样,Thread.interrupt()将不起作用,因为线程将不会退出被阻塞状态。Listing D描述了这一行为。尽管interrupt()被调用,线程也不会退出被阻塞状态 

 

Listing D 

 

 

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

很幸运,Java平台为这种情形提供了一项解决方案,即调用阻塞该线程的套接字的close()方法。在这种情形下,如果线程被I/O操作阻塞,该线程将接收到一个SocketException异常,这与使用interrupt()方法引起一个InterruptedException异常被抛出非常相似。 

唯一要说明的是,必须存在socket的引用(reference),只有这样close()方法才能被调用。这意味着socket对象必须被共享。Listing E描述了这一情形。运行逻辑和以前的示例是相同的。 

Listing E 

 

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

 

以下是运行Listing E中代码后的输出: 

Starting thread... 

Waiting for connection... 

Asking thread to stop... 

accept() failed or interrupted... 

Thread exiting under request... 

Stopping application... 

多线程是一个强大的工具,然而它正呈现出一系列难题。其中之一是如何中断一个正在运行的线程。如果恰当地实现,使用上述技术中断线程将比使用Java平台上已经提供的内嵌操作更为简单。

分享到:
评论

相关推荐

    09.多线程编程基础-停止线程-使用interrupt方法中断线程.mp4

    在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。

    JAVA线程停止的方法

    本文将深入探讨Java线程停止的方法,特别是通过一个示例代码片段来解析如何利用标志变量(Flag)控制线程的生命周期,以及这种方法背后的原理与最佳实践。 ### Java线程停止方法概述 在Java中,直接调用线程的`...

    java线程中的interrupt,isInterrupt,interrupted方法

    java 线程中的 interrupt,isInterrupt,interrupted 方法详解 在 Java 中,线程(Thread)类提供了三个相关的方法:interrupt、isInterrupted 和 interrupted,这三个方法都是用于处理线程的中断状态的。下面我们将...

    Java多线程编程线程的协同、停止、暂停、继续等操作实现

    Java提供了一种不推荐的方式来停止线程,即使用`Thread.stop()`,但这个方法不安全,因为它可能导致数据不一致和资源泄露。更好的做法是使用标志变量,如`volatile boolean stopRequested`,当主线程或其他线程想要...

    java线程中断之interrupt和stop.docx

    ### Java线程中断机制详解:`interrupt`与`stop`方法 #### 一、引言 在Java多线程编程中,线程控制是至关重要的技术之一。有时我们需要在特定条件下停止某个线程的执行,或者中断正在等待的线程。Java提供了多种...

    Java线程(第三版)

    书中还可能涉及异常处理和线程中断,`interrupt()`方法用于标记线程中断状态,`isInterrupted()`和`InterruptedException`用于检查和处理中断。中断机制是Java中优雅停止线程的关键。 另外,Java并发工具库(java....

    Java线程.ppt

    `start()`方法启动线程,`sleep()`方法让线程进入堵塞状态,`join()`方法等待线程执行完成,`yield()`方法让当前线程让出CPU,`stop()`方法停止线程(不推荐使用,因为可能引起数据不一致),`interrupt()`方法中断...

    JAVA线程学习(源代码)

    Java提供了多种方法来控制线程状态,如`start()`启动线程,`sleep()`使线程暂停,`join()`等待线程完成,以及`interrupt()`中断线程。 并发控制是线程编程中的重要部分。Java提供了synchronized关键字来实现互斥...

    java多线程Demo

    - interrupt()方法用于中断线程,如果线程正在阻塞(如sleep或wait),会被中断并抛出InterruptedException。 通过这些技术,我们可以构建高效、稳定、响应迅速的多线程应用程序。在实际开发中,应根据具体需求...

    java线程实例 各种小Demo

    Java提供了wait()、notify()和notifyAll()方法进行线程间的通信,但这需要在同步控制块(synchronized)中使用。Java 5引入了BlockingQueue阻塞队列,提供了一种更安全的线程间通信方式,如ArrayBlockingQueue、...

    java线程实战手册

    6. **线程中断与停止**:正确地停止线程是一项挑战,Java提供了interrupt()方法来请求线程中断,但需要注意的是,这并不一定能立即停止线程,需要配合中断标志进行检查和处理。 7. **线程池**:Executor框架和...

    java线程强制停止的两个Demo

    Java推荐使用更安全的中断机制,即通过`Thread.interrupt()`方法向线程发送中断信号,然后在线程的run方法中定期检查`isInterrupted()`或`interrupted()`状态来优雅地停止线程。这种方式允许线程清理资源并正常退出...

    java多线程的讲解和实战

    9. **线程中断**:`interrupt()`方法可以标记线程中断状态,线程可以通过检查`isInterrupted()`或`interrupted()`方法来响应中断请求,从而优雅地停止线程执行。 10. **线程局部变量(ThreadLocal)**:为每个线程...

    java线程与并发编程实践

    线程的生命周期包括新建、就绪、运行、阻塞和终止五种状态,Java API提供了start()、sleep()、join()、yield()、interrupt()等方法来控制线程状态。 并发编程的核心挑战在于线程间的同步与通信。Java提供了一系列...

    JAVA多线程操作方法实用大全

    本文将深入探讨Java中的多线程操作方法,包括线程控制的基本方法、中断和睡眠以及相关示例。 首先,了解线程的基本状态至关重要。线程在运行过程中可能处于新建、可运行、运行、阻塞或死亡等状态。`isAlive()`方法...

    Java线程学习和总结

    Java提供了一些方法来控制线程状态,如`sleep()`使线程暂停执行一段时间,`join()`等待指定线程结束,`yield()`让当前线程暂停,让其他相同优先级的线程有机会执行,`interrupt()`用于中断线程。 同步是Java线程...

    java线程.rar

    9. **线程中断**:通过`interrupt()`方法设置线程的中断标志,线程可以通过检查`isInterrupted()`或`interrupted()`方法来响应中断请求。 10. **线程Local变量**:`ThreadLocal`类为每个线程提供独立的变量副本,...

    电子书《java线程》

    8. **中断与异常**:线程可以通过interrupt()方法发送中断信号,以及如何处理InterruptedException。 9. **守护线程(Daemon)**:守护线程不会阻止程序退出,常用于后台服务。 10. **线程本地存储(ThreadLocal)...

    简单的java线程demo

    在Java线程中,还有一些关键的概念和方法: - `sleep()`: 让当前线程暂停执行指定的毫秒数,释放CPU资源,但不会释放锁。 - `join()`: 让当前线程等待指定线程完成执行。 - `isAlive()`: 检查线程是否处于活动状态...

    浅谈Java线程Thread之interrupt中断解析

    Java线程Thread之interrupt中断解析 Java线程Thread之interrupt中断机制是...Java线程Thread之interrupt中断机制是一种重要的机制,通过设置中断标记,可以控制线程的执行,并且可以避免使用stop方法对线程的中断。

Global site tag (gtag.js) - Google Analytics