如果线程需要执行一个长时间任务,就可能需要能中断线程。中断线程就是其他线程给该线程发一个信号,该线程收到信号后结束执行run()方法,使得自身线程能立刻结束运行。

我们举个栗子:假设从网络下载一个100M的文件,如果网速很慢,用户等得不耐烦,就可能在下载过程中点“取消”,这时,程序就需要中断下载线程的执行。

中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。

我们还是看示例代码:

// 中断线程

----

public class Main {

public static void main(String[] args) throws InterruptedException {

Thread t = new MyThread();

t.start();

Thread.sleep(1); // 暂停1毫秒

t.interrupt(); // 中断t线程

t.join(); // 等待t线程结束

System.out.println("end");

}

}

class MyThread extends Thread {

public void run() {

int n = 0;

while (! isInterrupted()) {

n ++;

System.out.println(n + " hello!");

}

}

}

仔细看上述代码,main线程通过调用t.interrupt()方法中断t线程,但是要注意,interrupt()方法仅仅向t线程发出了“中断请求”,至于t线程是否能立刻响应,要看具体代码。而t线程的while循环会检测isInterrupted(),所以上述代码能正确响应interrupt()请求,使得自身立刻结束运行run()方法。

如果线程处于等待状态,例如,t.join()会让main线程进入等待状态,此时,如果对main线程调用interrupt(),join()方法会立刻抛出InterruptedException,因此,目标线程只要捕获到join()方法抛出的InterruptedException,就说明有其他线程对其调用了interrupt()方法,通常情况下该线程应该立刻结束运行。

我们来看下面的示例代码:

// 中断线程

----

public class Main {

public static void main(String[] args) throws InterruptedException {

Thread t = new MyThread();

t.start();

Thread.sleep(1000);

t.interrupt(); // 中断t线程

t.join(); // 等待t线程结束

System.out.println("end");

}

}

class MyThread extends Thread {

public void run() {

Thread hello = new HelloThread();

hello.start(); // 启动hello线程

try {

hello.join(); // 等待hello线程结束

} catch (InterruptedException e) {

System.out.println("interrupted!");

}

hello.interrupt();

}

}

class HelloThread extends Thread {

public void run() {

int n = 0;

while (!isInterrupted()) {

n++;

System.out.println(n + " hello!");

try {

Thread.sleep(100);

} catch (InterruptedException e) {

break;

}

}

}

}

main线程通过调用t.interrupt()从而通知t线程中断,而此时t线程正位于hello.join()的等待中,此方法会立刻结束等待并抛出InterruptedException。由于我们在t线程中捕获了InterruptedException,因此,就可以准备结束该线程。在t线程结束前,对hello线程也进行了interrupt()调用通知其中断。如果去掉这一行代码,可以发现hello线程仍然会继续运行,且JVM不会退出。

另一个常用的中断线程的方法是设置标志位。我们通常会用一个running标志位来标识线程是否应该继续运行,在外部线程中,通过把HelloThread.running置为false,就可以让线程结束:

// 中断线程

----

public class Main {

public static void main(String[] args) throws InterruptedException {

HelloThread t = new HelloThread();

t.start();

Thread.sleep(1);

t.running = false; // 标志位置为false

}

}

class HelloThread extends Thread {

public volatile boolean running = true;

public void run() {

int n = 0;

while (running) {

n ++;

System.out.println(n + " hello!");

}

System.out.println("end!");

}

}

注意到HelloThread的标志位boolean running是一个线程间共享的变量。线程间共享变量需要使用volatile关键字标记,确保每个线程都能读取到更新后的变量值。

为什么要对线程间共享的变量用关键字volatile声明?这涉及到Java的内存模型。在Java虚拟机中,变量的值保存在主内存中,但是,当线程访问变量时,它会先获取一个副本,并保存在自己的工作内存中。如果线程修改了变量的值,虚拟机会在某个时刻把修改后的值回写到主内存,但是,这个时间是不确定的!

┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐

Main Memory

│ │

┌───────┐┌───────┐┌───────┐

│ │ var A ││ var B ││ var C │ │

└───────┘└───────┘└───────┘

│ │ ▲ │ ▲ │

─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─

│ │ │ │

┌ ─ ─ ┼ ┼ ─ ─ ┐ ┌ ─ ─ ┼ ┼ ─ ─ ┐

▼ │ ▼ │

│ ┌───────┐ │ │ ┌───────┐ │

│ var A │ │ var C │

│ └───────┘ │ │ └───────┘ │

Thread 1 Thread 2

└ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ┘

这会导致如果一个线程更新了某个变量,另一个线程读取的值可能还是更新前的。例如,主内存的变量a = true,线程1执行a = false时,它在此刻仅仅是把变量a的副本变成了false,主内存的变量a还是true,在JVM把修改后的a回写到主内存之前,其他线程读取到的a的值仍然是true,这就造成了多线程之间共享的变量不一致。

因此,volatile关键字的目的是告诉虚拟机:

每次访问变量时,总是获取主内存的最新值;

每次修改变量后,立刻回写到主内存。

volatile关键字解决的是可见性问题:当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改后的值。

如果我们去掉volatile关键字,运行上述程序,发现效果和带volatile差不多,这是因为在x86的架构下,JVM回写主内存的速度非常快,但是,换成ARM的架构,就会有显著的延迟。

小结

对目标线程调用interrupt()方法可以请求中断一个线程,目标线程通过检测isInterrupted()标志获取自身是否已中断。如果目标线程处于等待状态,该线程会捕获到InterruptedException;

目标线程检测到isInterrupted()为true或者捕获了InterruptedException都应该立刻结束自身线程;

通过标志位判断需要正确使用volatile关键字;

volatile关键字解决了共享变量在线程间的可见性问题。

python线程异常中断_中断线程相关推荐

  1. Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程

    Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程 参考文章: (1)Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动 ...

  2. 一个线程池中的线程异常了,那么线程池会怎么处理这个线程?

    一个线程池中的线程异常了,那么线程池会怎么处理这个线程? 参考文章: (1)一个线程池中的线程异常了,那么线程池会怎么处理这个线程? (2)https://www.cnblogs.com/fangua ...

  3. python两个线程交替打印_三线程按顺序交替打印ABC的四种方法

    建立三个线程A.B.C,A线程打印10次字母A,B线程打印10次字母B,C线程打印10次字母C,但是要求三个线程同时运行,并且实现交替打印,即按照ABCABCABC的顺序打印. 二.Synchroni ...

  4. python 使用异常函数_您如何测试Python函数引发异常?

    python 使用异常函数 This article elaborates on how to implement a test case for a function that raises an ...

  5. python检测异常数据_用Python中从头开始的实现完整的异常检测算法

    利用概率的异常检测算法 异常检测可以作为离群分析的统计任务来对待. 但是,如果我们开发一个机器学习模型,它可以自动化,并且像往常一样可以节省大量时间. 有很多异常检测用例. 信用卡欺诈检测,故障机器检 ...

  6. ScheduledExecutorService内线程异常导致任务中断

    今天遇到一个关于ScheduledExecutorService的诡异问题,执行ScheduledExecutorService.scheduleAtFixedRate()方法过后,这个定时任务只执行 ...

  7. python 多线程ping测试_从线程ping多个ip时Python ICMP ping实现?

    我一直在Windows上使用jedie's python ^{} implementation.我可能错了,但是当从不同的线程ping两台计算机(A和B)时,ping将返回它接收到的第一个ping,而 ...

  8. java修改线程池名称_自定义线程池的名称(ThreadPoolExecutor)

    目的:有时候为了快速定位出现错误的位置,在采用线程池时我们需要自定义线程池的名称. 1.创建ThreadFactory(ThreadPoolExecutor默认采用的是DefaultThreadFac ...

  9. 主线程 子线程死掉_当线程死时,子进程也会死

    我有一个程序可以触发Python计时器来生成子进程.一旦程序终止或终止,这些子进程就应该终止.为了做到这一点,我使用了"prctl hack",它设置了一旦父对象死亡,子对象应该接 ...

最新文章

  1. iOS 屏幕亮度和闪光灯控制
  2. idea cloud bootstrap是啥_application.yml与bootstrap.yml的区别
  3. python实现单张图像拼接与批量图片拼接
  4. php json 封装,laravel json返回封装
  5. cuda linux 算力_华为AI再进化,CANN 3.0释放算力狂魔
  6. 拓端tecdat|R语言 RevoScaleR的大规模数据集决策树模型应用案例
  7. java成员变量的调用_java中对象调用成员变量与成员的方法介绍
  8. Java面向对象游戏-黑熊怪变成武大郎吃唐僧 --如何方法继承 重写
  9. AC的集中和本地转发
  10. php计算排名,成绩相同需要并列算法
  11. first season tenth episode,Joey kissed Chandler!!!
  12. Unity Android Unable to load resource的问题
  13. CVPR 2021 | “以音动人”:姿态可控的语音驱动说话人脸
  14. 如何用ChatGPT制作PPT?
  15. cmd命令行使用pip install XXX库时,出现安装失败。
  16. 图机器学习——5.9 图神经网络:图的增广
  17. 【大数据实战】flume 数据采集
  18. 积分商城搭建前的5个关键准备工作?
  19. miui系统分身测试软件,【MIUI 8评测】手机分身初上手,黑科技值爆表!
  20. MATLAB——PCM编译码实验

热门文章

  1. ext常用iconCls 很实用
  2. 计算机专业c 用到的文献,【计算机专业论文】高校非计算机专业C程序设计的微课教学(共2539字)...
  3. spring学习笔记 -- day02 spring基础、IOC控制反转
  4. pvqc计算机类专业英语,PVQC专业英文词汇国际认证
  5. 调查问卷(单选、多选)
  6. 天津铁路工程投资控制系统-原来的文件打不开了,显示数据库错误
  7. Runnable、Callable、Future、RunnableFuture 和 FuturTask 到底是些啥,到底有啥关系?
  8. 新手如何学习c语言? 小马带你入门
  9. EDA开源仿真工具verilator入门1:安装和测试
  10. 红米10android auto,红米手机发展史,共10代机型,你用过其中哪几代