与此问题相关的内容主要涉及三部分:已废弃的Thread.stop()、迷惑的thread.interrupt系列、最佳实践Shared Variable。

已废弃的Thread.stop()


@Deprecated
public final void stop() {stop(new ThreadDeath());
}

如上是Hotspot JDK 7中的java.lang.Thread.stop()的代码,学习一下它的doc:

该方法天生是不安全的。使用thread.stop()停止一个线程,导致释放(解锁)所有该线程已经锁定的监视器(因沿堆栈向上传播的未检查异常ThreadDeath而解锁)。如果之前受这些监视器保护的任何对象处于不一致状态,则不一致状态的对象(受损对象)将对其他线程可见,这可能导致任意的行为。

是不是差点被这段话绕晕,俗点说:目标线程可能持有一个监视器,假设这个监视器控制着某两个值之间的逻辑关系,如var1必须小于var2,某一时刻var1等于var2,本来应该受保护的逻辑关系,不幸的是此时恰好收到一个stop命令,产生一个ThreadDeath错误,监视器被解锁。这就导致逻辑错误,当然这种情况也可能不会发生,是不可预料的。注意:ThreadDeath是何方神圣?是个java.lang.Error,不是java.lang.Exception。

public class ThreadDeath extends Error { private static final long serialVersionUID = -4417128565033088268L; } 

thread.stop()方法的许多应用应该由“只修改某些变量以指示目标线程应该停止”的代码取代。目标线程应周期性的检查该变量,当发现该变量指示其要停止运行,则退出run方法。如果目标线程等待很长时间,则应该使用interrupt方法中断该等待。

其实这里已经暗示停止一个线程的最佳方法:条件变量 或 条件变量+中断。

更多请查看:
Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?

上文请参考我的翻译xxx。

其它关于stop方法的doc:

  1. 该方法强迫停止一个线程,并抛出一个新创建的ThreadDeath对象作为异常。
  2. 停止一个尚未启动的线程是允许的,如果稍后启动该线程,它会立即终止。
  3. 通常不应试图捕获ThreadDeath,除非它必须执行某些异常的清除操作。如果catch子句捕获了一个ThreadDeath对象,则必须重新抛出该对象,这样该线程才会真正终止。

小结:
Thread.stop()不安全,已不再建议使用。

令人迷惑的thread.interrupt()


Thread类中有三个方法会令新手迷惑,他们是:

public void Thread.interrupt() // 无返回值
public boolean Thread.isInterrupted() // 有返回值 public static boolean Thread.interrupted() // 静态,有返回值 

如果按照近几年流行的重构,代码整洁之道,程序员修炼之道等书的观点,这几个方法的命名相对于其实现的功能来说,不够直观明确,极易令人混淆,是低级程序猿的代码。逐个分析:

public void interrupt() {if (this != Thread.currentThread())checkAccess();synchronized (blockerLock) {Interruptible b = blocker;if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); } 

中断本线程。无返回值。具体作用分以下几种情况:

  • 如果该线程正阻塞于Object类的wait()wait(long)wait(long, int)方法,或者Thread类的join()join(long)join(long, int)sleep(long)sleep(long, int)方法,则该线程的中断状态将被清除,并收到一个java.lang.InterruptedException
  • 如果该线程正阻塞于interruptible channel上的I/O操作,则该通道将被关闭,同时该线程的中断状态被设置,并收到一个java.nio.channels.ClosedByInterruptException
  • 如果该线程正阻塞于一个java.nio.channels.Selector操作,则该线程的中断状态被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用java.nio.channels.Selector.wakeup()方法一样。
  • 如果上述条件都不成立,则该线程的中断状态将被设置。

小结:第一种情况最为特殊,阻塞于wait/join/sleep的线程,中断状态会被清除掉,同时收到著名的InterruptedException;而其他情况中断状态都被设置,并不一定收到异常。

中断一个不处于活动状态的线程不会有任何作用。如果是其他线程在中断该线程,则java.lang.Thread.checkAccess()方法就会被调用,这可能抛出java.lang.SecurityException。

public static boolean interrupted() {return currentThread().isInterrupted(true);
}

检测当前线程是否已经中断,是则返回true,否则false,并清除中断状态。换言之,如果该方法被连续调用两次,第二次必将返回false,除非在第一次与第二次的瞬间线程再次被中断。如果中断调用时线程已经不处于活动状态,则返回false。

public boolean isInterrupted() {return isInterrupted(false);
}

检测当前线程是否已经中断,是则返回true,否则false。中断状态不受该方法的影响。如果中断调用时线程已经不处于活动状态,则返回false。

interrupted()与isInterrupted()的唯一区别是,前者会读取并清除中断状态,后者仅读取状态。

在hotspot源码中,两者均通过调用的native方法isInterrupted(boolean)来实现,区别是参数值ClearInterrupted不同。

private native boolean isInterrupted(boolean ClearInterrupted);

经过上面的分析,三者之间的区别已经很明确,来看一个具体案例,是我在工作中看到某位架构师的代码,只给出最简单的概要结构:

public void run() {while(!Thread.currentThread().isInterrupted()) {try {Thread.sleep(10000L);... //为篇幅,省略其它io操作 ... //为简单,省略其它interrupt操作 } catch (InterruptedException e) { break; } } } 

我最初被这段代码直接绕晕,用thread.isInterrupted()方法作为循环中止条件可以吗?

根据上文的分析,当该方法阻塞于wait/join/sleep时,中断状态会被清除掉,同时收到InterruptedException,也就是接收到的值为false。上述代码中,当sleep之后的调用otherDomain.xxx(),otherDomain中的代码包含wait/join/sleep并且InterruptedException被catch掉的时候,线程无法正确的中断。

因此,在编写多线程代码的时候,任何时候捕获到InterruptedException,要么继续上抛,要么重置中断状态,这是最安全的做法,参考『Java Concurrency in Practice』。凡事没有绝对,如果你可以确保一定没有这种情况发生,这个代码也是可以的。

下段内容引自:『Java并发编程实战』 第5章 基础构建模块 5.4 阻塞方法与中断方法 p77

当某个方法抛出InterruptedException时,表示该方法是一个阻塞方法。当在代码中调用一个将抛出InterruptedException异常的方法时,你自己的方法也就变成了一个阻塞方法,并且必须要处理对中断的相应。对于库代码来说,有两种选择:

  • 传递InterruptedException。这是最明智的策略,将异常传递给方法的调用者。
  • 恢复中断。在不能上抛的情况下,如Runnable方法,必须捕获InterruptedException,并通过当前线程的interrupt()方法恢复中断状态,这样在调用栈中更高层的代码将看到引发了一个中断。如下代码是模板:
public void run() {try {// ① 调用阻塞方法} catch (InterruptedException e) {Thread.currentThread().interrupt();    // ② 恢复被中断的状态 } } 

最后再强调一遍,②处的 Thread.currentThread().interrupt() 非常非常重要。

最佳实践:Shared Variable


不记得哪本书上曾曰过,最佳实践是个烂词。在这里这个词最能表达意思,停止一个线程最好的做法就是利用共享的条件变量。

对于本问题,我认为准确的说法是:停止一个线程的最佳方法是让它执行完毕,没有办法立即停止一个线程,但你可以控制何时或什么条件下让他执行完毕。

通过条件变量控制线程的执行,线程内部检查变量状态,外部改变变量值可控制停止执行。为保证线程间的即时通信,需要使用使用volatile关键字或锁,确保读线程与写线程间变量状态一致。下面给一个最佳模板:

/*** @author bruce_sha (bruce-sha.github.io)* @version 2013-12-23*/
public class BestPractice extends Thread { private volatile boolean finished = false; // ① volatile条件变量 public void stopMe() { finished = true; // ② 发出停止信号 } @Override public void run() { while (!finished) { // ③ 检测条件变量 // do dirty work // ④业务代码 } } } 

本文尚未完成,请耐心等待。


当④处的代码阻塞于wait()或sleep()时,线程不能立刻检测到条件变量。因此②处的代码最好同时调用interrupt()方法。

小结:
How to Stop a Thread or a Task ? 详细讨论了如何停止一个线程, 总结起来有三点:

  1. 使用violate boolean变量来标识线程是否停止。
  2. 停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性。
  3. 对于blocking IO的处理,尽量使用InterruptibleChannel来代替blocking IO。

总结:


要使任务和线程能安全、快速、可靠地停止下来,并不是一件容易的事。Java没有提供任何机制来安全地终止线程。但它提供了中断(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的的工作。—— 『Java并发编程实战』 第7章 取消与关闭 p111

中断是一种协作机制。一个线程不能强制其它线程停止正在执行的操作而去执行其它的操作。当线程A中断B时,A仅仅是要求B在执行到某个可以暂停的地方停止正在执行的操作——前提是如果线程B愿意停下来。—— 『Java并发编程实战』 第5章 基础构建模块 p77

总之,中断只是一种协作机制,需要被中断的线程自己处理中断。停止一个线程最佳实践是 中断 + 条件变量。

转载于:https://www.cnblogs.com/azhqiang/p/4048512.html

如何停止一个正在运行的java线程相关推荐

  1. 腾讯面试官:如何停止一个正在运行的线程?我蒙了。。。

    以下文章来源方志朋的博客,回复"666"获面试宝典 停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作.停止一个线程可以用Thread.stop()方法, ...

  2. 面试官:如何停止一个正在运行的线程?我一脸蒙蔽...

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:cnblogs.com/greta/p/5624839.ht ...

  3. 如何停止一个正在运行的线程?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | cnblogs.com/greta/p/562 ...

  4. 腾讯面试官:如何停止一个正在运行的线程?我一脸蒙蔽。。。

    停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作.停止一个线程可以用Thread.stop()方法,但最好不要用它.虽然它确实可以停止一个正在运行的线程,但是这个方法是不安 ...

  5. tomcat线程循环异常终止_腾讯面试官:如何停止一个正在运行的线程?我一脸蒙蔽。。。...

    1. 停止不了的线程 2. 判断线程是否停止状态 3. 能停止的线程--异常法 4. 在沉睡中停止 5. 能停止的线程---暴力停止 6.方法stop()与java.lang.ThreadDeath异 ...

  6. scheduledexecutorservice 只执行一次_一个ScheduledExecutorService启动的Java线程无故挂掉引发的思考...

    2018年12月12日18:44:53 案件现场 不久前,在开发改造公司一个端到端监控日志系统的时候,出现了一个bug:有个扫表写日志的线程无故挂掉. 顺藤摸瓜 我看了很久的代码,都没有想出来有什么地 ...

  7. python 事件通知模式_请问在 Python 的事件系统中,如何可以通过事件通知立刻终结一个正在运行的子线程?...

    大家好,最近在完善手头上一个基于事件系统的 GUI . 现在遇到一个问题,就是当我在执行一个按钮点击事件的时候,实际会开一个子线程进行业务逻辑的处理,这个处理过程可能会比较长,并且中间可能会出现一些不 ...

  8. android如何终止一个正在运行的子线程

    安卓的线程Run方法,执行一次就自动退出了,可以加入循环实现在Run方法中持续运行 线程像这样: Thread{boolean flag = false;run(){while(!flag){}} } ...

  9. java 如何结束线程_java中,如何安全的结束一个正在运行的线程?

    问题 Java中提供了很多调度线程的方法,上一节介绍了其中一种控制线程的方法:如何等待一个线程结束.那么如果不希望等待线程结束,而是根据问题的需要随时都要中断线程使其结束,这种对线程的控制方法该如何实 ...

最新文章

  1. 独家 | 一文读懂序列建模(deeplearning.ai)
  2. Access数据库查询练习专用数据库 -手机号码归属地Access数据库_MobileDB(10万条记录)...
  3. bzoj 4551[Tjoi2016Heoi2016]树
  4. BootstrapTable分页(二)
  5. 【Spring】Spring 自动注入(autowire)详解
  6. 8分钟搞清波士顿动力进化史 | 视频
  7. Windows CE的存储与地址映射技术学习
  8. SSH访问远程主机超时问题解决
  9. 牛客多校第九场 ZOJ3774 The power of Fibonacci(二次剩余定理+斐波那契数列通项/循环节)题解...
  10. iwebAx产品家族之iweb SNS v0.6体验版--不错的开源软件~~
  11. VLOOKUP函数使用方法详解
  12. 调整Node运行内存全攻略
  13. [PAL规范]SAP HANA PAL 数据处理四分位间距检测Inter-quartile Range Test编程规范IQRTEST...
  14. 【功能上新】Python实现OSM地图数据解析——OSM2Rail
  15. 用一台电脑连接另一台电脑的虚拟机
  16. Prompt-Guided Injection of Conformation to Pre-trained Protein Model
  17. Springboot实现发送邮箱
  18. Python小工具-电影天堂爬取电影下载链接
  19. Java如何计算年龄
  20. FTP主动模式和被动模式讲解

热门文章

  1. ESXI6.5 最新版尝鲜安装图解
  2. C#创建WinForm应用程序的入口点
  3. SSH连接不成功的处理过程
  4. 人生致命的8个经典问题,你也常常犯傻
  5. android 设置PopupWindow的显示大小
  6. Visual formatting model(图)
  7. WebKit 内核源码分析 (二) FrameLoader
  8. 【Android.mk】android编译系统makefile文件Android.mk的写法
  9. 三十分钟掌握STL(Using STL)
  10. Fragment 与DialogFragment 相互之间传递数据