java 线程关闭小结(转)
首先,要说的是java中没有一种停止线程的方法是绝对安全的.
线程的中断
Thread.interrput()方法很容易给人一种误会,让人感觉是一个线程使另外一个正在运行的线程停止工作,
但实际上interrput仅仅传递了请求中断的信息.线程自己会在下一个方便的时间中断.
某些操作会接受这个请求时发出一个异常,比如wait,sleep.每一个Thread线程都有一个中断状态,是boolean型的当调用interrput方法后会使中断为true.
当使用的静态的interrputed时可以清除中断状态,也就是说连续调用2次interrputed会将中断状态变为false.isInterrupted方法可以返回中断状态.
JDK1.6 API:
interrupted
public static boolean interrupted()Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it).
A thread interruption ignored because a thread was not alive at the time of the interrupt will be reflected by this method returning false.
Returns:
true if the current thread has been interrupted; false otherwise.
See Also:
有时候调用interrput方法会出现异常而且中断状态没有改变,你可以在异常处理catch块中使用interrupted=true重试.
Java中断线程的时候分两种情况:第一种是在线程正常执行时,第二种是在线程阻塞时.
(1) 正常线程运行时一般会运行一个较长的任务有的会在while(true)中,这种线程最好中断只需要在循环中加入一个if(){break};判断就好.while判断条件是中断状态.嘿.
(2)在线程阻塞的时候进程不会执行其他任何命令,所以不能改变判断状态.这时候使用Thread.interrupt()方法产生一个InterruptedException运行时异常,在catch块中,跑出一个运行时异常(RuntimeException)或使用break;这种方法可以适用于{ Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中阻塞的线程,能看出来这些方法都会跑出InterruptedException异常}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
break;
thrownew RuntimeException();
}
(3)你不可以中断试图获取synchronized锁但是由于锁被占用未被释放而被挂起的线程或者试图执行I/O操作的线程.
a) 对于IO异常
i. 对于被I/O读写阻塞的线程嘴简单直接的方法是调用基础资源的close()方法.也就是读写的close方法.
(旧IO不会发出InterruptedException异常但是使用close方法会跑出IOException异常同上使用相同方法可以跳出循环中断线程)
ii. 在新版本的nio类会自动响应中断,发出IOException异常
b) 对于由于互斥堵塞.使用ReentrantLock对象加锁的线程可以被终结,使用ReentrantLock的lockInterruptibly方法,如果当前线程未被中断,则获取锁这就可以中断被互斥的锁。
c)处理反常的线程终止
在并发程序中,发生异常而未能捕获异常这使得线程执行失败,但是往往这种失败,不会影响程序的继续执行.
导致失败的主要原因还是RuntimeException这种异,通常这种异常是不能捕获的.
如何捕获这些异常呢.
javaAPI提供了这一类问题的解决办法,提供了Thread.UncaughtExceptionHandler(异常处理器具体查看JDK)使你能够检测到线程因未捕获而引起的线程死亡.
可以使用Thread的set方法设置异常处理器.
如果不存在异常处理器则使用System.err输出.
PS:在运行时间较长时间的线程里,为线程提供一个异常处理器是有必要的,至少在这个处理器中记录日志.
为了给线程池中的每个线程都加入异常处理器,可以在建造线程池的时候给定一个ThreadFactory.为每个线程池内的Thread加入异常处理器.
PS.线程池只有通过execute方法才会将异常交给异常处理器.
通过submit方式提交的会使用get方法重新给出异常.
使用异常处理器后不会使用system.err向控制台写入异常.
http://blog.csdn.net/joker_zhou/article/details/7322692
总的来说有3种:
1.使用状态位,这个简单,就不多说了:
public class Task extends Thread {
private volatile boolean flag= true;
public void stopTask() {
flag = false;
}
@Override
public void run() {
while(flag){
/* do your no-block task */
}
}
}
2.当线程等待某些事件发生而被阻塞,又会发生什么?
当然,如果线程被阻塞,它便不能核查共享变量,也就不能停止。这在许多情况下会发生,例如调用 Object.wait()、Thread.sleep、Thread.join等需要抛出InterruptedException的代码块,这里仅举出一些。他们都可能永久的阻塞线程。即使发生超时,在超时期满之前持续等待也是不可行和不适当的,所以,要使用某种机制使得线程更早地退出被阻塞的状态。这个时候你可以使用Thread.interrupt();
JDK1.6 API:
public void interrupt()Interrupts this thread.
Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.
If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.
If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.
public class BlockTask extends Thread {
@Override
public void run() {
try {
while (!Thread.interrupted()) {
/* do your block task*/
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
但是上面的代码或许有些不妥,或许用例子更能把问题说清楚。你怎么知道该代码段会发生阻塞?interrupt()函数到底是什么意思呢?
首先说明的是,interrupted()方法只能解决跑出InterruptedException异常的阻塞。而interrupt()并不是关闭阻塞线程,而且解除阻塞。
那这里就举出一个关闭线程阻塞的例子把:
public class BlockTask extends Thread {
@Override
public void run() {
try {
sleep(10000);
} catch (InterruptedException e) {
System.out.println("if yout use interrupt you will see me");
}
}
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
BlockTask task = new BlockTask();
task.start();
Thread.sleep(1000);
task.interrupt();
}
}
3.上面说了,interrupt()只能解决InterruptedException的阻塞的线程,那么遇到一些其他的io阻塞怎么处理呢?这个时候java都会提供相应的关闭阻塞的办法。
例如,服务器可能需要等待一个请求(request),又或者,一个网络应用程序可能要等待远端主机的响应,这个时候可以使用套接字close()方法
public class SocketTask extends Thread {
private volatile ServerSocket server;
public void stopTask(){
try {
if(server!=null){
server.close();
System.out.println("close task successed");
}
} catch (IOException e) {
System.out.println("close task failded");
}
}
@Override
public void run() {
try {
server = new ServerSocket(3333);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
SocketTask task = new SocketTask();
task.start();
Thread.sleep(1000);
task.stopTask();
}
}
详细说明
程序是很简易的。然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决,将导致意外的行为以及细微的、难以发现的错误。
在本篇文章中,我们针对这些难题之一:如何中断一个正在运行的线程。
背景
中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。
线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序。
虽然初次看来它可能显得简单,但是,你必须进行一些预警以实现期望的结果。你最好还是牢记以下的几点告诫。
首先,忘掉Thread.stop方法。虽然它确实停止了一个正在运行的线程,然而,这种方法是不安全也是不受提倡的,这意味着,在未来的JAVA版本中,它将不复存在。
一些轻率的家伙可能被另一种方法Thread.interrupt所迷惑。尽管,其名称似乎在暗示着什么,然而,这种方法并不会中断一个正在运行的线程(待会将进一步说明),正如Listing A中描述的那样。它创建了一个线程,并且试图使用Thread.interrupt方法停止该线程。Thread.sleep()方法的调用,为线程的初始化和中止提供了充裕的时间。
线程本身并不参与任何有用的操作。
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..." );
}
}如果你运行了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
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,而且要求更多的工作。既然这样,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平台上已经提供的内嵌操作更为简单。
http://blog.csdn.net/Yuzhiyuxia/article/details/8837621
java 线程关闭小结(转)的更多相关文章
- 从Tomcat无法正常关闭讲讲Java线程关闭问题【转载】
正常情况下,会优先采用catalina.sh stop来停止Tomcat实例,这样可以让服务有机会处理完请求,并做好善后工作. 但如果通过catalina.sh stop命令无法关闭Tomcat实例, ...
- java线程同步小结
1.线程同步的目的是为了防止多个线程同时访问一个资源时对资源的破坏 2.线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无 ...
- 如何优雅的关闭Java线程池
面试中经常会问到,创建一个线程池需要哪些参数啊,线程池的工作原理啊,却很少会问到线程池如何安全关闭的. 也正是因为大家不是很关注这块,即便是工作三四年的人,也会有因为线程池关闭不合理,导致应用无法正常 ...
- Java线程:概念与原理
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- java线程详解
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Java线程详解----借鉴
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- java 线程(1)
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Java 线程池比较
小结: 1. 高级面试题总结—线程池还能这么玩? - 这个时代,作为程序员可能要学习小程序 - CSDN博客https://blog.csdn.net/androidstarjack/article/ ...
- java线程大全一讲通
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
随机推荐
- Android JNI programming demo with Eclipse
用Eclipse 建立 JNI 的專案, 示範怎样在 JAVA 調用 cpp 的函數. 我們將建立一個名稱為 jnidemo的專案, 在主Activity 將調用一個名為libHello.so 的 c ...
- Entity - 使用EF框架进行增删改查 - 模型先行
模型先行:先创建数据库实体模型,然后再进行数据库的增删改查. 基本步骤是不变的,可参照 <Entity - 使用EF框架进行增删改查 - 数据库先行> 其中的不同是,在创建数据库实体模型的 ...
- Android开发之查看应用包名package和入口activity名称的方法
使用android自动化测试工具monkeyrunner启动应用时,需要填写被测程序的包名和启动的Activity,以下有两种查看应用包名package和入口activity名称的方法: 方法一:使用 ...
- Spring的事件处理
Spring对事件有一些支持,因为项目须要,所以近期小小研究了下究竟这个怎么能够方便的用在实际项目其中来. 说起事件这个东西,事实上就是借鉴的那个观察者模式.这里面涉及到事件源.事件监听者.事件公布者 ...
- 分分钟教会你使用HTML写Web页面
在学习怎样使用HTML编写网页之前,我们必须先搞清楚什么是HTML?当然了不是系统的给大家介绍HTML的前世今生,假设对其身世感兴趣的小伙伴能够去问度娘,她会给你想要的答案. 所谓HTML,就是我们常 ...
- 【从零学习经典算法系列】分治策略实例——高速排序(QuickSort)
在前面的博文(http://blog.csdn.net/jasonding1354/article/details/37736555)中介绍了作为分治策略的经典实例,即归并排序.并给出了递归形式和循环 ...
- 屏幕对象的F1/F4输入帮助功能
1.HELP-REQUST[FOR{LOW|HIGH}]字段的F1帮助 当选择SAP屏幕功能的制定字段按F1键时可以调关注用自定义的程序或者系统帮助文件,该功能通常称为F1帮助. TYPES:syst ...
- .NET Core R2
.NET Core R2安装及示例教程 前言 前几天.NET Core发布了.NET Core 1.0.1 R2 预览版,之前想着有时间尝试下.NET Core.由于各种原因,就没有初试.刚好,前 ...
- VC中添加web控件的方法
在VC中使用WebBrowser控件的两方法 黄森堂(vcmfc)著 ClassWizard方式: 1.创建包装类:View->ClassWizard->Add Class->For ...
- android之LruCache源代码解析
移动设备开发中,因为移动设备(手机等)的内存有限,所以使用有效的缓存技术是必要的.android提供来一个缓存工具类LruCache,开发中我们会经经常使用到,以下来他是怎样实现的. 在package ...