- 浏览: 26253 次
- 性别:
- 来自: 泰安
文章分类
最新评论
使用Java内置支持的线程写多线程程序是很常见的事情。然而,多线程给开发人员带来了一些新的挑战。如果处理不好就会导致超出预期的行为以及难于定位的错误。这篇文章解读了其中一个挑战:如何中止一个正在运行的线程。
背景
中止一个线程意味着在线程处理完任务之前停掉正在做的操作,特别是放弃当前的操作。之后无论线程死掉,等待新的任务,或者执行下一步取决于应用程序的逻辑。
虽然这看起来非常简单,但是你必须做好防范措施,以便达到预期的效果。而且有些提醒你必须注意。
首先,忘记Thread.stop 方法吧。虽然他确实可以停掉一个正在运行的线程,但是这个方法是不安全的(unsafe)且是被弃用的(deprecated)。这意味着在将来的Java版本中,这个方法将不可用。
另一个容易迷惑的方法是Thread.interrupt。尽管名字是中止,但这个方法不会终止一个正在运行的线程(后面还会分析)。例1描述这个现象。这个例子中创建了一个线程,然后尝试使用Thread.interrupt去停止线程。例子中Thread.sleep留出充足时间供线程初始化和终止。线程本身不做任何有用的事情。
例1: Stopping a thread with 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 ) {
}
}
}
}
如果你运行例1的代码,你会在终端上看到下面的东西:
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()被调用后,线程仍然会继续运行一段时间。
真正地中止一个线程
最好的,也是推荐的中止线程的方式是使用一个共享变量去通知线程停掉正在做的事情。线程必须周期性地检测这个变量,特别是执行时间较长操作时,然后有序地停掉当前的任务。例2给出了这种技术。
例2: Signaling a thread to stop
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..." );
}
}
运行例2的代码会得到如下的输出(注意线程如何以预期的方式结束):
Starting thread...
Thread is running...
Thread is running...
Thread is running...
Asking thread to stop...
Thread exiting under request...
Stopping application...
虽然这个方法需要编写一些代码,但并不难实现,且给了线程处理任何清理工作的机会。这对任何多线程应用来说都是绝对需要的。注意一定要把共享变量设置为volatile,或者将访问他的操纵封装到synchronized代码块/方法中。
到现在为止,实现的已经很好了。但是如果线程被阻塞住等待一些事件呢?显然如果线程被阻塞,就不能检测共享变量,也就不能停掉。很多场景会导致线程被阻塞,举几个例子,如调用Object.wait(),ServerSocket.accept(),及DatagramSocket.receive()等。
他们都可以一直阻塞线程。即使设置了超时时间,等待超时过期也未必是可行的或期望的结果。所以必须使用一种可以提前退出阻塞状态的机制。
然后并不存在适用所有场景的机制,特定的技术只能用在特定的场景下。下面的篇幅中,我会给出大部分常见场景的方案。
使用Thread.interrupt()终止线程
正如例1中的描述,Thread.interrupt()方法并不能中止正在运行的线程。这个方法真正的效果是如果线程被阻塞,则抛出一个中止异常,这样线程退出阻塞状态。更确切的说,如果被阻塞在Object.wait,Thread.join或者Thread.sleep其中一个方法上,线程会收到一个InterruptedException异常,这样提前结束阻塞线程的方法。
所以,如果线程被上述的某个方法阻塞住,正确的终止方法是先设置共享变量,然后调用interrupt()方法(注意一定要先设置变量)。如果线程没有被阻塞,则调用interrupt()没有坏处;否则线程会获取一个异常(线程必须准备好处理这个场景)并推出阻塞状态。无论哪种情况下,最后线程会检测共享变量并终止。例3是描述这种技术的一个简单例子。
例3: Exiting blocked states with 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..." );
}
}
例3中,一旦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可能阻塞一个线程很长一段时间,特别是涉及网络通讯。例如,一个server在等待请求,或者一个网络应用在等待远端服务器的应答。
如果你在使用Java 1.4中引入的新I/O API,被阻塞的线程会得到一个ClosedByInterruptException异常。这种情况下,逻辑上与第三个例子相同,只是异常不同。
由于新 I/O最近才引入且需要更多的工作,你可能还在使用Java 1.0引入的传统I/O。这种情况下,Thread.interrupt()没有效果,所以线程不会退出阻塞状态。例4说明了这种行为。虽然interrupt()被调用,但线程没有退出阻塞状态。
例4: Interrupting I/O operations with Thread.interrupt()
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平台为这种场景提供了一个方案,即调用阻塞线程的socket的close()方法。这样线程会得到一个SocketExcpetion异常,类似于interrupt()导致抛出InterruptedException()。
唯一要注意的是socket的引用必须可以获取,以调用close()方法。也意味着socket对象必须也是共享的。例5描述了这种场景,逻辑跟目前为止给出的例子一样。
[java] view plaincopy
例5: Causing I/O operations to fail
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..." );
}
}
这是运行例5的期望输出:
Starting thread...
Waiting for connection...
Asking thread to stop...
accept() failed or interrupted...
Thread exiting under request...
Stopping application...
多线程是个强大的工具,但也带来了自己的挑战。其中一个就是如果中止正在运行的线程。如果合理的实现,这些技术可以使得中止一个线程和使用Java平台已经提供的内置操作一样简单。
背景
中止一个线程意味着在线程处理完任务之前停掉正在做的操作,特别是放弃当前的操作。之后无论线程死掉,等待新的任务,或者执行下一步取决于应用程序的逻辑。
虽然这看起来非常简单,但是你必须做好防范措施,以便达到预期的效果。而且有些提醒你必须注意。
首先,忘记Thread.stop 方法吧。虽然他确实可以停掉一个正在运行的线程,但是这个方法是不安全的(unsafe)且是被弃用的(deprecated)。这意味着在将来的Java版本中,这个方法将不可用。
另一个容易迷惑的方法是Thread.interrupt。尽管名字是中止,但这个方法不会终止一个正在运行的线程(后面还会分析)。例1描述这个现象。这个例子中创建了一个线程,然后尝试使用Thread.interrupt去停止线程。例子中Thread.sleep留出充足时间供线程初始化和终止。线程本身不做任何有用的事情。
例1: Stopping a thread with 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 ) {
}
}
}
}
如果你运行例1的代码,你会在终端上看到下面的东西:
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()被调用后,线程仍然会继续运行一段时间。
真正地中止一个线程
最好的,也是推荐的中止线程的方式是使用一个共享变量去通知线程停掉正在做的事情。线程必须周期性地检测这个变量,特别是执行时间较长操作时,然后有序地停掉当前的任务。例2给出了这种技术。
例2: Signaling a thread to stop
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..." );
}
}
运行例2的代码会得到如下的输出(注意线程如何以预期的方式结束):
Starting thread...
Thread is running...
Thread is running...
Thread is running...
Asking thread to stop...
Thread exiting under request...
Stopping application...
虽然这个方法需要编写一些代码,但并不难实现,且给了线程处理任何清理工作的机会。这对任何多线程应用来说都是绝对需要的。注意一定要把共享变量设置为volatile,或者将访问他的操纵封装到synchronized代码块/方法中。
到现在为止,实现的已经很好了。但是如果线程被阻塞住等待一些事件呢?显然如果线程被阻塞,就不能检测共享变量,也就不能停掉。很多场景会导致线程被阻塞,举几个例子,如调用Object.wait(),ServerSocket.accept(),及DatagramSocket.receive()等。
他们都可以一直阻塞线程。即使设置了超时时间,等待超时过期也未必是可行的或期望的结果。所以必须使用一种可以提前退出阻塞状态的机制。
然后并不存在适用所有场景的机制,特定的技术只能用在特定的场景下。下面的篇幅中,我会给出大部分常见场景的方案。
使用Thread.interrupt()终止线程
正如例1中的描述,Thread.interrupt()方法并不能中止正在运行的线程。这个方法真正的效果是如果线程被阻塞,则抛出一个中止异常,这样线程退出阻塞状态。更确切的说,如果被阻塞在Object.wait,Thread.join或者Thread.sleep其中一个方法上,线程会收到一个InterruptedException异常,这样提前结束阻塞线程的方法。
所以,如果线程被上述的某个方法阻塞住,正确的终止方法是先设置共享变量,然后调用interrupt()方法(注意一定要先设置变量)。如果线程没有被阻塞,则调用interrupt()没有坏处;否则线程会获取一个异常(线程必须准备好处理这个场景)并推出阻塞状态。无论哪种情况下,最后线程会检测共享变量并终止。例3是描述这种技术的一个简单例子。
例3: Exiting blocked states with 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..." );
}
}
例3中,一旦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可能阻塞一个线程很长一段时间,特别是涉及网络通讯。例如,一个server在等待请求,或者一个网络应用在等待远端服务器的应答。
如果你在使用Java 1.4中引入的新I/O API,被阻塞的线程会得到一个ClosedByInterruptException异常。这种情况下,逻辑上与第三个例子相同,只是异常不同。
由于新 I/O最近才引入且需要更多的工作,你可能还在使用Java 1.0引入的传统I/O。这种情况下,Thread.interrupt()没有效果,所以线程不会退出阻塞状态。例4说明了这种行为。虽然interrupt()被调用,但线程没有退出阻塞状态。
例4: Interrupting I/O operations with Thread.interrupt()
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平台为这种场景提供了一个方案,即调用阻塞线程的socket的close()方法。这样线程会得到一个SocketExcpetion异常,类似于interrupt()导致抛出InterruptedException()。
唯一要注意的是socket的引用必须可以获取,以调用close()方法。也意味着socket对象必须也是共享的。例5描述了这种场景,逻辑跟目前为止给出的例子一样。
[java] view plaincopy
例5: Causing I/O operations to fail
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..." );
}
}
这是运行例5的期望输出:
Starting thread...
Waiting for connection...
Asking thread to stop...
accept() failed or interrupted...
Thread exiting under request...
Stopping application...
多线程是个强大的工具,但也带来了自己的挑战。其中一个就是如果中止正在运行的线程。如果合理的实现,这些技术可以使得中止一个线程和使用Java平台已经提供的内置操作一样简单。
发表评论
-
log temple xml
2014-04-11 10:12 644<?xml version="1.0&qu ... -
class proxy
2014-03-06 16:53 7831.Proxy.newProxyInstance(source ... -
[转]Java多线程总结之由synchronized说开去
2012-11-19 16:22 689Java多线程总结之由synchr ... -
JES服务程序
2012-11-13 14:02 759JES,是Java嵌入服务器-Java Embedded Se ... -
[转]深入Java对象及元素的存储区域
2012-10-15 09:46 656在JAVA平台上开发应 ... -
【JAVA】Concurrent
2012-10-11 15:59 832讲到Java多线程,大多数人脑海中跳出来的是Thread、Ru ... -
【转】【JAVA】 hashcode相关
2012-10-11 15:20 631有许多人学了很长时间 ...
相关推荐
线程组可以包含线程和其他线程组,可以设置权限,也可以进行统一的中断或中止操作。 总之,Java线程是并发编程的基础,理解和掌握线程的生命周期、实现方式和同步机制对于编写高效、安全的多线程程序至关重要。在...
线程的状态包括新建、可运行、运行、阻塞、中止五种状态。线程的状态间的转换是通过start、sleep、wait、notify等方法实现的。 线程的优先级 线程的优先级是指线程的执行优先级,越高的优先级越先执行。线程的...
4. **阻塞状态**:一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行费时的输入输出操作时,将让出CPU并暂时中止自己的执行,进入阻塞状态。阻塞时它不能进入排队队列。 5. **死亡状态**:处于死亡状态...
### 多线程之06三种中止线程的方法比较 #### 一、引言 在Java编程中,线程管理是一项重要的技能。有时我们需要在特定条件下终止正在运行的线程,但如何做到这一点既安全又高效呢?本文将探讨三种常见的线程中止...
当线程中抛出未捕获的异常时,除非线程被显式地中止,否则它将继续执行。因此,线程应该有自己的异常处理机制,通常通过Thread.UncaughtExceptionHandler接口来实现。 总之,Java多线程编程涉及的内容广泛,包括...
6. **TERMINATED(中止)**:线程已完成执行或因为未被捕获的异常而提前结束,此时线程不再处于任何活动状态。 #### 线程状态之间的转换 线程状态之间的转换主要由以下几个方面决定: - **从NEW到RUNNABLE**:...
Java 多线程教程之如何使用线程池详解 Java 多线程教程中,线程池是一种非常重要的概念。在 Java 中,线程池是指一个线程的集合,可以重复使用这些线程来执行任务。使用线程池可以提高服务器的性能和可扩展性。本文...
可以通过界面按钮来启动和中止线程的运行。这里涉及的关键知识点包括: 1. **线程的创建**:在Java中,有两种主要方式创建线程。一种是通过继承`Thread`类,重写`run()`方法,然后创建`Thread`对象并调用`start()`...
Java中的线程中断是指在某个线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。这种操作可能会使线程死亡、等待新的任务或是继续运行至下一步。中断线程需要注意一些细微的难题,如果没有被恰当的...
* kill:中止线程 * interrupt:中断线程 * print:输出表达式的值 * dump:输出所有对象信息 * eval:输出表达式的值 * set:为字段/变量/数组元素指定新值 * locals:输出当前堆栈帧中的所有局部变量 * classes:...
在Java中,异步编程的注意点还包括对线程池的合理使用,因为异步操作通常需要在多个线程之间协调,而线程池是管理线程生命周期的有效方式。异步API的实现通常依赖于线程池来提高性能,但也需要注意线程池大小的合理...
因此,我们需要采取安全的方式来中止线程。本文将深入探讨如何在Android环境中安全地中断一个自定义线程Thread。 首先,我们需要了解Java线程的基本中断机制。每个线程都有一个中断状态,可以通过`Thread.interrupt...
正确的线程中止-interrupt 如果目标线程在调用Object class的wait()、wait(long)或wait(long millis, int nanos)方法或wait(long millis, int nanos)方法、join()、join(long millis, int nanos)或s
// 用于中止线程的run()方法 in_message.setText("串口COM1已经关闭,停止发送数据."); } else { mark = false; data = out_message.getText().getBytes(); start(); in_message.setText("串口COM1已经打开,...
JAVA精确定时器,利用系统时间,使长期工作的误差稳定。 功能: ·可定时启动任务或直接启动任务 ·重复启动任务(时间间隔可在任务线程中改变,范围大于100ms,否则精度降低) 引用列表: ·import psn.razerpen....
以上是Java面试中常见的问题及答案,涵盖了操作系统交互、字符串处理、多线程、网络请求、图形绘制、浮点数精度、数据库操作以及页面控制等多个方面。理解和掌握这些知识点对提升Java开发者的技能水平至关重要。
// 用于中止线程的 run() 方法 in_message.setText("串口已关闭"); } } } ``` ### 六、总结 本篇文章详细介绍了Java串口通信的基础知识,包括所需的库文件、配置方法以及简单的程序示例。通过本文的学习,...
在Java应用程序中,Quartz能够帮助我们在特定的时间点或按照预设的周期执行某些功能,如数据收集、日志清理等。当我们需要终止某个正在运行的任务时,了解如何在Quartz中操作是至关重要的。 首先,让我们详细讲解...
这些JAVA面试题涵盖了多个方面的知识,包括操作系统交互、字符串处理、多线程、网络编程、JSP、异常处理、运算符、图形绘制、浮点数精度、数据库操作、缓存控制、接口概念以及JavaScript的基本操作。下面是对这些...
【Java面试知识点详解】 ...以上是70个Java面试题中部分涉及的知识点详解,这些题目涵盖了Java语言基础、Web开发、多线程、网络通信、输入输出、数值计算等多个方面,是Java开发者必备的基础技能。