中断Java线程

由于可能导致异常行为的产生,多线程技术显然对于开发人员来说提出了一系列新的挑战。本文,我们将就这些挑战之一:如何中断一个正在运行的线程展开讨论。

在Java中通过其内建的线程支持,编写多线程的程序还是相当简单的。然而,采用多线程技术将对程序开发人员提出了一些列的挑战,如果没有得到正确的处理,可能会导致异常行为的产生,以及难以发现的差错。本文,我们将就这些挑战之一:如何中断一个正在运行的线程展开讨论。

背景
中断一个线程意味着在完成其任务以前,停止线程正在进行的工作,即有效的中止当前操作。线程中断后是等待新的任务还是继续进行下一步操作将取决于应用程序。

尽管在最初看起来比较简单,你还是需要预先采取一些措施以求获得理想的结果。这里就你必须注意的问题提出了一些建议:

首先,不要使用Thread.stop方法。尽管它的确可以中止一个正在运行的线程,但这样的方法并不安全,并遭到了开发人员普遍的反对。这也可能意味着在未来的Java版本中它可能不会出现。

另一种并不建议的方法是Thread.interrupt。有人可能会将其与上文提到的方法相混淆。不论它的名字表示什么,这种方法事实上并没有立即中断一个正在运行的线程(后来也不会),如列表A所示。它创建了一个线程,并且尝试使用Thread.interrupt来停止此线程。对Thread.sleep()的调用提供了充裕的时间来进行线程的初始化和结束。线程本身并没有做任何有用的事情。

如果运行列表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()之后,线程还是运行了一段时间。

真正的中断一个线程
中断一个线程的最好的推荐方法是使用一个共享变量来指示线程必须中止目前所做的工作。线程必须周期性的对变量进行检查,尤其在处理较长的操作的时候,然后通过有序的方式中止线程任务。列表B的代码给出了此技术的具体实现:

运行列表B中的代码将会产生如下输出(注意线程在前一种方法中是如何退出的):

Starting thread...

Thread is running...

Thread is running...

Thread is running...

Asking thread to stop...

Thread exiting under request...

Stopping application...

尽管这样的方法需要编写一定量代码,但这并不会给执行这些线程以及根据需要对线程进行清除带来多大麻烦,而尤其是清除线程对于任何一个多线程的应用程序来说都是绝对必需的。只需要确保已经对共享变量声明为可变,或者将任何对其的访问封装在同步代码块或方法里面。

到现在为止,一切都很顺利。但如果线程被封锁以等待一些事件,那么将会发生些什么?当然,如果线程被封锁,它将不能对共享变量进行检查,从而无法停止。有很多时候会发生这样的情况,诸如对Object.wait()、ServerSocket.accept()以及DatagramSocket.receive()瞪函数进行调用的时候。

这些函数都能够将线程永远的封锁起来。即使采用了超时机制,或许也不会可行,或者让人无法忍受线程一直运行直到到达超时状态。所以必须采用某种机制以使线程提早的退出封锁状态。

不幸的是,这里还没有这样的机制能够适用于所有的情况。但可以根据具体的情况来使用一些特定的技巧。在如下的部分,我将给出对于绝大部分的常见情况所采取的解决方案。

通过Thread.interrupt()中断一个线程

如列表A所示,采用Thread.interrupt()方法并没有中断一个正在运行的线程。此方法事实上做的只是如果线程被封锁则抛出一个中断信号,由此线程退出了封锁状态。更为精确的讲,如果线程被封锁在方法Object.wait、Thread.join或是Thread.sleep,它将接收一个InterruptedException,从而提前终结封锁方法。

因此,如果一个线程被封锁在上述方法中的任意一个,停止它的正确方法是设置共享变量,并对其调用interrupt()方法(注意首先设置变量非常重要)。如果线程没有被锁定,调用interrupt()则无关紧要,否则,线程将会得到一个异常(线程本身必须准备好处理这种情况)然后退出锁定状态。在任何一种情况中,最终线程都将会检测共享变量并终止。列表C的简单范例程序表明了这一技术的运用。

一旦Thread.interrupt()在列表C代码中得到调用,线程将获得一个异常,于是它退出了封锁状态并决定它应当停止。运行这些代码将输出一下结果:

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可以在一段可观的时间里保持对一个线程的封锁,尤其涉及网络通信的时候。比如,一个服务器可能会等待用户请求,或者一个网络应用程序会等待远程主机的响应。

如果你正在使用通道——在Java 1.4中可以获得的新的I/O API——封锁的线程将会得到一个ClosedByInterruptException异常。如果是这种情况,处理的逻辑方法与在第三个例子中所使用的相同——不同的仅仅是产生的异常有所区别。

但是,由于新的I/O最近才发布并且还有待进一步研究,你也可能还在使用Java 1.0以来一直提供的传统的I/O。这种情况下,使用Thread.interrupt()不会产生任何帮助,原因是线程将不会退出封锁状态。列表D中的代码显示了这一过程。尽管调用了interrupt()方法,线程还是没有退出封锁状态。

值得庆幸的是,Java Platform提供了在这种情况下的解决方案:通过调用锁定线程的socket的close()方法。这样的情况下,如果线程是在进行I/O操作时被锁定,线程将得到一个SocketException异常,这非常类似于interrupt()方法引发了InterruptedException异常的情况。

唯一需要提醒的地方是对socket的引用必须可用,这样的话close()方法才能被调用。这也意味着socket对象也必须被共享。列表E显示了这种情况。处理的逻辑过程与以前给出的范例相同。

运行列表E中代码将会得到如下预期结果:

Starting thread...

Waiting for connection...

Asking thread to stop...

accept() failed or interrupted...

Thread exiting under request...

Stopping application...

多线程是功能非常强大的工具,但也给自身的处理带来了一些列挑战。其中之一便是如何中断一个正在运行的线程。如果处理得当,使用这些技术中断线程的难度将不会大于使用Java Platform提供的内建操作完成同样的任务。

列表A:通过Thread.interrupt()中断线程

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

}

public void run() {

while ( true ) {

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

long time = System.currentTimeMillis();

while ( System.currentTimeMillis()-time < 1000 ) {

}

}

}

}

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

}

}

列表C:通过Thread.interrupt()退出封锁状态

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

}

}

列表D:通过Thread.interrupt()中断I/O操作

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

}

}

}

}

列表E:I/O操作失败

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

}

}

时间: 2024-11-30 05:25:29

中断Java线程的相关文章

如何中断JAVA线程

如何中断JAVA线程 程序是很简易的.然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决,将导致意外的行为以及细微的.难以发现的错误.       在本篇文章中,我们针对这些难题之一:如何中断一个正在运行的线程.                                                                                      背景     中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,

Java Thread.interrupt 害人! 中断JAVA线程

程序是很简易的.然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决,将导致意外的行为以及细微的.难以发现的错误.       在本篇文章中,我们针对这些难题之一:如何中断一个正在运行的线程.                                                                                      背景     中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作

Java线程中断的本质深入理解

    Java的中断是一种协作机制.也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己. 一.Java中断的现象 首先,看看Thread类里的几个方法:  public static boolean interrupted 测试当前线程是否已经中断.线程的中断状态 由该方法清除.换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况

Java线程中断的本质深入理解(转)

  一.Java中断的现象 首先,看看Thread类里的几个方法: public static boolean interrupted 测试当前线程是否已经中断.线程的中断状态 由该方法清除.换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外). public boolean isInterrupted() 测试线程是否已经中断.线程的中断状态 不受该方法的影响. public void 

Java线程中断的本质深入理解_java

一.Java中断的现象 首先,看看Thread类里的几个方法: public static boolean interrupted 测试当前线程是否已经中断.线程的中断状态 由该方法清除.换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外). public boolean isInterrupted() 测试线程是否已经中断.线程的中断状态 不受该方法的影响. public void in

Java线程模型缺陷研究

Java 编程语言的线程模型可能是此语言中最薄弱的部分.它完全不适合实际复杂程序的要求,而且也完全不是面向对象的.本文建议对 Java 语言进行重大修改和补充,以解决这些问题. Java 语言的线程模型是此语言的一个最难另人满意的部分.尽管 Java 语言本身就支持线程编程是件好事,但是它对线程的语法和类包的支持太少,只能适用于极小型的应用环境. 关于 Java 线程编程的大多数书籍都长篇累牍地指出了 Java 线程模型的缺陷,并提供了解决这些问题的急救包(Band-Aid/邦迪创可贴)类库.我

Java线程机制(五) 等待与通知机制

在之前我们关于停止Thread的讨论中,曾经使用过设定标记done的做法,一旦done设置为true,线程就会 结束,一旦为false,线程就会永远运行下去.这样做法会消耗掉许多CPU循环,是一种对内存不友好的行为. java中的对象不仅拥有锁,而且它们本身就可以通过调用相关方法使自己成为等待者和通知者. Object对象本身有两个方法:wait()和notify().wait()会等待条件的发生,而notify()会通知正在 等待的线程此条件已经发生,它们都必须从synchronized方法或

Java线程机制(三) synchronized和volatile的使用

现在开始进入线程编程中最重要的话题---数据同步,它是线程编程的核心,也是难点,就算我们理解了 数据同步的基本原理,但是我们也无法保证能够写出正确的同步代码,但基本原理是必须掌握的. 要 想理解数据同步的基本原理,首先就要明白,为什么我们要数据同步? public class CharacterDisplayCanvas extends JComponent implements CharacterListener { protected FontMetrics fm; protected ch

java线程学习总结

何时使用多线程技术,以及何时避免用它,这是我们需要掌握的重要课题.骼它的主要目的是对大量任务进行有序的管理.通过多个任务的混合使用,可以更有效地利用计算机资源,或者对用户来说显得更方便.资源均衡的经典问题是在IO等候期间如何利用CPU.至于用户方面的方便性,最经典的问题就是如何在一个长时间的下载过程中监视并灵敏地反应一个"停止"(stop)按钮的按下. 多线程的主要缺点包括: (1) 等候使用共享资源时造成程序的运行速度变慢. (2) 对线程进行管理要求的额外CPU开销. (3) 复杂