第二篇 多线程的使用——中断线程详解(Interrupt)

简介:

 在上篇文章《多线程的使用——Thread类和Runnable接口》中提到中断线程的问题。在JAVA中,曾经使用stop方法来停止线程,然而,该方法具有固有的不安全性,因而已经被抛弃(Deprecated)。那么应该怎么结束一个进程呢?官方文档中对此有详细说明:《为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?》。在此引用stop方法的说明:

1. Why is Thread.stop deprecated?
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.
大概意思是:
因为该方法本质上是不安全的。停止一个线程将释放它已经锁定的所有监视器(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其他线程可见,这有可能导致任意的行为。此行为可能是微妙的,难以察觉,也可能是显著的。不像其他的未检查异常,ThreadDeath异常会在后台杀死线程,因此,用户并不会得到警告,提示他的程序可能已损坏。这种损坏有可能在实际破坏发生之后的任何时间表现出来,也有可能在多小时甚至在未来的很多天后。
在文档中还提到,程序员不能通过捕获ThreadDeath异常来修复已破坏的对象。具体原因见原文。
既然stop方法不建议使用,那么应该用什么方法来代理stop已实现相应的功能呢?
 
1、通过修改共享变量来通知目标线程停止运行
 
大部分需要使用stop的地方应该使用这种方法来达到中断线程的目的。
 
这种方法有几个要求或注意事项:
(1)目标线程必须有规律的检查变量,当该变量指示它应该停止运行时,该线程应该按一定的顺序从它执行的方法中返回。
(2)该变量必须定义为volatile,或者所有对它的访问必须同步(synchronized)。
 
例如:
假如你的applet包括start,stop,run几个方法:
 

 
 
  1. private Thread blinker; 
  2.  
  3. public void start() { 
  4.     blinker = new Thread(this); 
  5.     blinker.start(); 
  6.  
  7. public void stop() { 
  8.     blinker.stop();  // UNSAFE! 
  9.  
  10. public void run() { 
  11.     Thread thisThread = Thread.currentThread(); 
  12.     while (true) { 
  13.         try { 
  14.         thisThread.sleep(interval); 
  15.         } catch (InterruptedException e){ 
  16.         } 
  17.         repaint(); 
  18.     } 
你可以使用如下方式避免使用Thread.stop方法:

 
 
  1. private volatile Thread blinker; 
  2.  
  3. public void stop() { 
  4.     blinker = null
  5.  
  6. public void run() { 
  7.     Thread thisThread = Thread.currentThread(); 
  8.     while (blinker == thisThread) { 
  9.         try { 
  10.             thisThread.sleep(interval); 
  11.         } catch (InterruptedException e){ 
  12.         } 
  13.         repaint(); 
  14.     } 
 
2、通过Thread.interrupt方法中断线程
 
通常情况下,我们应该使用第一种方式来代替Thread.stop方法。然而以下几种方式应该使用Thread.interrupt方法来中断线程(该方法通常也会结合第一种方法使用)。
一开始使用interrupt方法时,会有莫名奇妙的感觉:难道该方法有问题?
API文档上说,该方法用于"Interrupts this thread"。请看下面的例子:

 
 
  1. package com.polaris.thread; 
  2.  
  3. public class TestThread implements Runnable{ 
  4.  
  5.     boolean stop = false
  6.     public static void main(String[] args) throws Exception { 
  7.         Thread thread = new Thread(new TestThread(),"My Thread"); 
  8.         System.out.println( "Starting thread..." ); 
  9.         thread.start(); 
  10.         Thread.sleep( 3000 ); 
  11.         System.out.println( "Interrupting thread..." ); 
  12.         thread.interrupt(); 
  13.         System.out.println("线程是否中断:" + thread.isInterrupted()); 
  14.         Thread.sleep( 3000 ); 
  15.         System.out.println("Stopping application..." ); 
  16.     } 
  17.     public void run() { 
  18.         while(!stop){ 
  19.             System.out.println( "My Thread is running..." ); 
  20.             // 让该循环持续一段时间,使上面的话打印次数少点 
  21.             long time = System.currentTimeMillis(); 
  22.             while((System.currentTimeMillis()-time < 1000)) { 
  23.             } 
  24.         } 
  25.         System.out.println("My Thread exiting under request..." ); 
  26.     } 
运行后的结果是:
Starting thread...
My Thread is running...
My Thread is running...
My Thread is running...
My Thread is running...
Interrupting thread...
线程是否中断:true
My Thread is running...
My Thread is running...
My Thread is running...
Stopping application...
My Thread is running...
My Thread is running...
……
应用程序并不会退出,启动的线程没有因为调用interrupt而终止,可是从调用isInterrupted方法返回的结果可以清楚地知道该线程已经中断了。那位什么会出现这种情况呢?到底是interrupt方法出问题了还是isInterrupted方法出问题了?在Thread类中还有一个测试中断状态的方法(静态的)interrupted,换用这个方法测试,得到的结果是一样的。由此似乎应该是interrupt方法出问题了。于是,在网上有一篇文章:《 Java Thread.interrupt 害人! 中断JAVA线程》,它详细的说明了应该如何使用interrupt来中断一个线程的执行。
实际上,在JAVA API文档中对该方法进行了详细的说明。该方法实际上只是设置了一个中断状态,当该线程由于下列原因而受阻时,这个中断状态就起作用了:
(1)如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个InterruptedException异常。这个时候,我们可以通过捕获InterruptedException异常来终止线程的执行,具体可以通过return等退出或改变共享变量的值使其退出。
(2)如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。这时候处理方法一样,只是捕获的异常不一样而已。
 
其实对于这些情况有一个通用的处理方法:

 
 
  1. package com.polaris.thread; 
  2.  
  3. public class TestThread2 implements Runnable{ 
  4.  
  5.     boolean stop = false
  6.     public static void main(String[] args) throws Exception { 
  7.         Thread thread = new Thread(new TestThread2(),"My Thread2"); 
  8.         System.out.println( "Starting thread..." ); 
  9.         thread.start(); 
  10.         Thread.sleep( 3000 ); 
  11.         System.out.println( "Interrupting thread..." ); 
  12.         thread.interrupt(); 
  13.         System.out.println("线程是否中断:" + thread.isInterrupted()); 
  14.         Thread.sleep( 3000 ); 
  15.         System.out.println("Stopping application..." ); 
  16.     } 
  17.     public void run() { 
  18.         while(!stop){ 
  19.             System.out.println( "My Thread is running..." ); 
  20.             // 让该循环持续一段时间,使上面的话打印次数少点 
  21.             long time = System.currentTimeMillis(); 
  22.             while((System.currentTimeMillis()-time < 1000)) { 
  23.             } 
  24.             if(Thread.currentThread().isInterrupted()) { 
  25.                 return
  26.             } 
  27.         } 
  28.         System.out.println("My Thread exiting under request..." ); 
  29.     } 
因为调用interrupt方法后,会设置线程的中断状态,所以,通过监视该状态来达到终止线程的目的。
 
总结:程序应该对线程中断作出恰当的响应。响应方式通常有三种:(来自温绍锦(昵称:温少):http//www.cnblogs.com/jobs/)

注意:interrupted与isInterrupted方法的区别(见API文档)

 

 

引用一篇文章

来自随心所欲http://redisliu.blog.sohu.com/131647795.html 的《Java的interrupt机制》

当外部线程对某线程调用了thread.interrupt()方法后,java语言的处理机制如下:

如果该线程处在可中断状态下,(调用了xx.wait(),或者Selector.select(),Thread.sleep()等特定会发生阻塞的api),那么该线程会立即被唤醒,同时会受到一个InterruptedException,同时,如果是阻塞在io上,对应的资源会被关闭。如果该线程接下来不执行“Thread.interrupted()方法(不是interrupt),那么该线程处理任何io资源的时候,都会导致这些资源关闭。当然,解决的办法就是调用一下interrupted(),不过这里需要程序员自行根据代码的逻辑来设定,根据自己的需求确认是否可以直接忽略该中断,还是应该马上退出。

如果该线程处在不可中断状态下,就是没有调用上述api,那么java只是设置一下该线程的interrupt状态,其他事情都不会发生,如果该线程之后会调用行数阻塞API,那到时候线程会马会上跳出,并抛出InterruptedException,接下来的事情就跟第一种状况一致了。如果不会调用阻塞API,那么这个线程就会一直执行下去。除非你就是要实现这样的线程,一般高性能的代码中肯定会有wait(),yield()之类出让cpu的函数,不会发生后者的情况。




     本文转自polaris1119 51CTO博客,原文链接:http://blog.51cto.com/polaris/372146,如需转载请自行联系原作者


相关文章
|
5月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
261 0
|
5月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
6月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
453 5
|
10月前
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
407 20
|
10月前
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
236 1
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
265 3
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
148 2
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
218 2
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
419 0

热门文章

最新文章