有些时候会需要在线程的run()方法返回之前手动中止线程,这时就会用到Thread的interrupt()实例方法。这个方法的使用有许多坑,稍不注意就会出错。本文将对这个方法以及与它相关的知识点进行一下梳理。

interrupt()与isInterrupted()

非常常用的两个方法。顾名思义,它们是分别用于中断线程以及检查线程的中断状态的。说实话,正是因为它们的名字取得太简单了,用之前很少有人会去仔细看它的说明文档,这就导致了一大堆的问题。事实上,一看文档就会发现这两个方法里面隐藏了不少的坑。
下面是interrupt()方法的文档的一部分:

     * <p> If this thread is blocked in an invocation of the {@link* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link* Object#wait(long, int) wait(long, int)} methods of the {@link Object}* class, or of the {@link #join()}, {@link #join(long)}, {@link* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},* methods of this class, then its interrupt status will be cleared and it* will receive an {@link InterruptedException}.

翻译一下就是,如果线程由于调用Object类的wait()方法、Thread类的join()实例方法以及Thread.sleep()静态方法而被挂起,此时其他线程调用了这个线程的interrupt()方法,那么这个线程的中断状态会被清除,并会抛出一个InterruptedException异常。
从上面加粗的文字中可以得出两点结论:
(1)即便你调用了interrupt()方法,你在之后用isInterrupted()方法检查它的中断状态时也不一定能得到true。
(2)如果线程当前运行处的代码块不对InterruptedException异常进行合适的处理,那么interrupt()方法就没有任何效果。
下面举一个例子来验证上面的结论:

public abstract class StoppableThread extends Thread {@Overridepublic void run() {while(!isInterrupted()){System.out.println("isInterrupted: " + isInterrupted());doWork();}System.out.println("END");}protected abstract void doWork();public static void main(String[] args) {StoppableThread thread = new StoppableThread() {@Overrideprotected void doWork() {System.out.println("running...");try {sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}};thread.start();try {Thread.sleep(2200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("即将调用interrupt()方法");thread.interrupt();}
}

这个例子看上去好像没任何问题,它在run方法中设置了一个while循环不断检查isInterrupted()的返回值,返回值为false则继续,否则跳出循环。
下面是它的运行输出结果:

isInterrupted: false
running...
isInterrupted: false
running...
isInterrupted: false
running...
isInterrupted: false
running...
isInterrupted: false
running...
即将调用interrupt()方法
isInterrupted: false
running...
java.lang.InterruptedException: sleep interruptedat java.lang.Thread.sleep(Native Method)at readerandwriter.StoppableThread$1.doWork(StoppableThread.java:22)at readerandwriter.StoppableThread.run(StoppableThread.java:9)
isInterrupted: false
running...
isInterrupted: false
running...

结果很匪夷所思吧,主线程调用子线程的interrupt()方法后,非但线程没有停止,而且isInterrupted()方法居然还是返回false。这就是上面的结论(1)和结论(2)组合起来造成的后果。
可能有人会问,既然interrupt()这么不可靠,那干脆就不用这个方法了,靠别的方法来控制循环的出入不就好了吗?答案是不行的,因为如果线程处于挂起状态(wait()、sleep()等),那么常规方法是没办法让它醒过来的,自然也就退出不了。这种情况下就必须要依赖抛出的InterruptedException。

InterruptedException

单看名字很容易认为这个异常是在调用了interrupt()方法后抛出的。事实上,interrupt()方法所做的仅仅是设置了一下Thread对象的中断状态而已,InterruptedException是在线程的wait()、sleep()、join()等方法中抛出的。
下面来验证一下。把上面的那个类的doWork()方法改一改,让它声明抛出InterruptedException,并把实现中的sleep(500)去掉。

public abstract class StoppableThread extends Thread {@Overridepublic void run() {while(!isInterrupted()){System.out.println("isInterrupted: " + isInterrupted());try {doWork();} catch (InterruptedException e) {System.out.println("InterruptedException is Thrown");}}System.out.println("END");}protected abstract void doWork() throws InterruptedException;public static void main(String[] args) {StoppableThread thread = new StoppableThread() {@Overrideprotected void doWork() throws InterruptedException{System.out.println("running...");}};thread.start();try {Thread.sleep(2200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("即将调用interrupt()方法");thread.interrupt();}
}

输出结果如下:

isInterrupted: false
running...
isInterrupted: false
running...
isInterrupted: false
running...
isInterrupted: false
running...
isInterrupted: false
running...
即将调用interrupt()方法
isInterrupted: false
running...
END

可以看到,虽然线程结束了,但是InterruptedException异常并没有被抛出(如果抛出了会输出一行InterruptedException is Thrown)。线程是因为isInterrupted()返回true才退出循环的。结合上面interrupt()方法的文档,可以得出两条结论:
(1)interrupt()仅仅设置了Thread对象的中断状态,InterruptedException是在线程的wait()、sleep()、join()等方法中抛出的,并且抛出后会清除掉Thread对象的中断状态。
(2)当线程正在执行一些常规方法时,即便调用了interrupt()方法也不会抛出InterruptedException,但会导致isInterrupted()方法返回true。

Thread.interrupted()

Thread还有一个不太常用的静态方法interrupted(),用来检查当前线程的中断状态的。需要注意的是,这个方法除了返回当前线程的中断状态(true/false),还会把中断状态给清除掉。也就是说,如果你连着调用两次这个方法,那么第二次必然返回的是false,因为无论中断状态原本是什么,它都已经被第一次的Thread.interrupted()清除了。

如何安全可靠地中断线程

那么怎样才能安全可靠地中断线程呢?事实上,似乎没有一个完美的方法能够实现在调用某个方法的瞬间就让线程退出。一个比较好的方法是在Thread子类中添加一个标志位,这里命名为isShutdownRequested,默认为false,并提供一个方法,用于在interrupt()的同时设置它为true。最后,在线程的run()方法的while循环控制条件中利用这个标志位判断线程是否被要求中止。
下面提供了一个StoppableThread实现,里面包含一个shutdown()方法,用于中止线程:

public abstract class StoppableThread extends Thread {private volatile boolean isShutdownRequested = false;public final void shutdown(){isShutdownRequested = true;interrupt();}public final boolean isShutdownRequested() {return isShutdownRequested;}@Overridepublic void run() {try {while(!isShutdownRequested()){doWork();}} catch (InterruptedException e) {System.out.println("InterruptedException is Thrown");} finally {doShutdown();}}protected abstract void doWork() throws InterruptedException;protected abstract void doShutdown();public static void main(String[] args) {StoppableThread thread = new StoppableThread() {@Overrideprotected void doWork() throws InterruptedException {System.out.println("running...");sleep(500);}@Overrideprotected void doShutdown() {System.out.println("shutdown!");}};thread.start();try {Thread.sleep(2200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("即将调用shutdown()方法");thread.shutdown();}
}

下面是输出结果:

running...
running...
running...
running...
running...
即将调用shutdown()方法
InterruptedException is Thrown
shutdown!

可以看到,线程通过InterruptedException成功地退出了。
使用上面这个类时,需要注意以下几点:
(1)需要实现doWork()(每次循环都会调用)与doShutdown()(线程退出前调用)方法。
(2)如果doWork()是一个耗时长的常规方法,那么调用shutdown()后不会马上退出,而会等待这个方法返回。
(3)不能在doWork()方法中处理掉InterruptException,否则可能导致线程退出不了。

Thread的interrupt()方法排雷相关推荐

  1. Java Thread的interrupt方法详解

    Java Thread的interrupt方法详解 一.概述 interrupt方法的目的是给线程发出中断信号,但是不保证线程真的会中断 中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应 ...

  2. 从一道面试题分析Thread.interrupt方法

    阿里面试题: public class TestThread {public static void main(String[] args) {Thread t1 = new Thread() {@O ...

  3. Thread.interrupt()方法理解

    多线程编程 不客气地说,至少有一半人认为,线程的"中断"就是让线程停止. 如果你也这么认为,那你对多线程编程还没有入门. 在java中,线程的中断(interrupt)只是改变了线 ...

  4. Java Thead.interrupt 方法没有使线程停止工作

    有问题的interrupt package com.xinyu.test;public class TestInterrupt {public static void main(String[] ar ...

  5. 【代码】使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应

    import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concu ...

  6. 停止Java线程,小心interrupt()方法

    转自http://www.blogjava.net/jinfeng_wang/archive/2008/04/27/196477.html 程序是很简易的.然而,在编程人员面前,多线程呈现出了一组新的 ...

  7. Java中Thread类的方法简介

    1.新建线程 Thread t1 = new Thread(); t1.start(); 新建线程,应该调用start()方法启动线程:如果直接调用run()方法,该方法也会执行,但会被当做一个普通的 ...

  8. java多线程基本概述(二)——Thread的一些方法

    在Thread类中有很多方法值得我们关注一下.下面选取几个进行范例: 1.1.isAlive()方法 java api 描述如下: public final boolean isAlive() Tes ...

  9. 一心多用多线程-Thread的interrupt机制

    使Thread中断的方式有很多种,比如调用stop()方法进行中断,但stop()方法存在线程的安全性问题,java api已经不推荐使用,那么我们想要让一个线程停止,我们可以使用java里面的int ...

最新文章

  1. Flink1.4.0中反序列化及序列化类变化
  2. JZOJ 5197. 【NOIP2017提高组模拟7.3】C
  3. mysql+地图网格数据下载_echarts 中国各省市 echarts地图数据,含世界地图
  4. css3制作八棱锥_CSS3制作各种形状图像
  5. django 模型增加字段后迁移失败
  6. 程序员必杀技——《编程全能词典》即将震憾上市
  7. openwrt源码下载移植
  8. 机器人操作系统——ROS,Robot Operating System
  9. 知识点收录04:MAVEN相关的知识点
  10. 微服务面试题及详细答案
  11. 当前上下文中不存在名称 ViewBag
  12. pm8060raid卡命令
  13. 如何远程办公电脑 关于电脑远程办公的方法和工具分享
  14. 江西省谷歌高清卫星地图下载
  15. 如何用excel做正交分析_利用Excel进行正交设计及分析.pdf
  16. excel文件下载下来损坏 js_js读取本地excel文件出现问题,这是咋回事
  17. 论文学习笔记: Learning Multi-Scale Photo Exposure Correction(含pytorch代码复现)
  18. OA厂商的硬实力—技术水平
  19. 【私人订制Python模块库】Pyemail:更方便快捷地发送电子邮件
  20. 在MATLAB中手动安装MinGW64详细教程

热门文章

  1. 手机android能打开吗,安卓手机能打开eif文件吗 手机怎么打开eip文件方法
  2. 意外的收获!分享U盘数据丢失后的u盘恢复工具!
  3. 手动打造优盘杀毒伴侣
  4. 共享软件为何要走向国际--月光博客
  5. 20145226夏艺华 《Java程序设计》实验报告二
  6. Photoshop-八步教你设计扁平化图标阴影部分
  7. 【wikioi】2495 水叮当的舞步(IDA*)
  8. 倩女幽魂2稳定的服务器,《倩女幽魂2》服务器帮会形势分析之点将台篇
  9. Excel按某一列或多列合并行
  10. 基于Java日月星养老院管理系统的设计与实现