Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何停止/中断一个运行中的线程

如何停止/中断一个运行中的线程

作者头像
用户1516716
发布于 2020-06-17 11:44:22
发布于 2020-06-17 11:44:22
3.3K00
代码可运行
举报
文章被收录于专栏:A周立SpringCloudA周立SpringCloud
运行总次数:0
代码可运行

# 面试题:

  • 如何正确地停止/中断一个运行中的线程
  • 哪些情况下线程会停止
  • 如何处理不可中断的阻塞

# 核心思想

  • 使用interrupt()来通知,而不是强制。

# 代码演示

  • 场景1:run()方法中没有sleep()/wait()等会响应中断的方法。 1.1 线程未处理中断:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 正确停止线程---run()方法内没有sleep()或者wait()方法-未处理中断信号
 *
 * @author futao
 * @date 2020/6/6
 */
public class StopThreadWithoutSleepWait implements Runnable {

    @Override
    public void run() {
        unHandleInterrupt();
    }

    /**
     * 未处理中断
     */
    public void unHandleInterrupt() {
        int num = 0;
        //打印最大整数一半的范围内10000的倍数
        while (num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是10000倍数");
            }
            ++num;
        }
        System.out.println("任务执行完毕");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThreadWithoutSleepWait());
        //启动线程
        thread.start();
        //增加子线程处于运行状态的可能性
        Thread.sleep(500L);
        //尝试中断子线程
        thread.interrupt();
    }
}
  • 期望:子线程在执行500毫秒之后停下来。
  • 结果:线程并没有停下来。原因是:我们并未处理线程的中断信号。

1.2 对程序进行改进:响应中断。

  • 在while循环条件中判断当前线程是否被中断(Thread.currentThread().isInterrupted()),如果未被中断才继续执行,被中断则跳出while循环。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.futao.learn.threads.c_如何停止线程;

/**
 * 正确停止线程---run()方法内没有sleep()或者wait()方法
 *
 * @author futao
 * @date 2020/6/6
 */
public class StopThreadWithoutSleepWait implements Runnable {

    @Override
    public void run() {
        handleInterrupt();
    }

    /**
     * 响应中断
     */
    public void handleInterrupt() {
        int num = 0;
        //加入线程未被中断的条件
        while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是10000倍数");
            }
            ++num;
        }
        System.out.println("任务执行完毕");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThreadWithoutSleepWait());
        //启动线程
        thread.start();
        //增加子线程处于运行状态的可能性
        Thread.sleep(500L);
        //尝试中断子线程
        thread.interrupt();
    }
}
  • 期望:线程在500毫秒之后响应中断,停下来。
  • 结果:线程成功响应中断,提前结束。
  • 总结可得出:线程调用者可以向线程发出中断请求,但是线程中断的权利控制在线程代码的编写者是否响应了你的中断请求。线程代码的编写者比调用者更加了解线程应不应该被停止,何时停止。
  • 场景2:run()方法中存在sleep()/wait()等会响应中断的方法。(响应中断的方法会抛出InterruptedException) 2.1 sleep()在while循环外
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 中断线程-run()方法中有sleep()或者wait()方法
 *
 * @author futao
 * @date 2020/6/6
 */
public class StopThreadWithSleep {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            int num = 0;
            while (!Thread.currentThread().isInterrupted() && num <= 300) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的整数倍");
                }
                ++num;
            }
            try {
                //sleep()方法会响应中断,且响应中断的方式为抛出InterruptException异常--- sleep interrupted
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程执行完毕");
        });
        //启动线程
        thread.start();
        //等待while循环执行完毕
        Thread.sleep(200L);
        //当线程处于sleep()状态时进行中断
        thread.interrupt();
    }
}
  • 预期:程序执行完while循环之后,阻塞在sleep()方法,此时进行中断,sleep()方法响应该中断,抛出InterruptedException,打印异常堆栈。
  • 测试:符合预期。

2.2 无法停止的线程:sleep()方法在while循环内。

  • 你预期下面代码的执行结果是怎样的?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 3. 无法停止的线程
 *
 * @author futao
 * @date 2020/6/6
 */
public class CantStopThread {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            int num = 1;
            while (num <= 1000 && !Thread.currentThread().isInterrupted()) {
                if (num % 2 == 0) {
                    System.out.println(num + "是2的整数倍");
                }
                ++num;
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行完毕");
        });

        //启动线程
        thread.start();
        //主线程休眠500毫秒
        Thread.sleep(500L);
        //中断线程
        thread.interrupt();
    }
}
  • 预期:线程在第一次进入while循环时,进入休眠1000毫秒状态,在500毫秒时主线程向子线程发出中断信号,sleep()方法响应中断,打印异常堆栈,下次再进入while循环时,因为线程被设置成了中断状态,所以while中条件不成立,不应该继续执行。 但是实际上是这样吗?
  • 结果:slee()响应了中断,打印了异常堆栈。但是线程并没有停下来,而是继续执行。就像什么都没有发生一样。
  • 原因:sleep()在响应了中断之后,清除了线程的中断状态。那么while判断时不知道线程被中断了。
  • 查看sleep()方法的描述:当InterruptedException异常被抛出后,线程的中断状态将被清除。
  • 类似的,查看Object.wait()的方法描述。
  • 类似的会响应中断的方法还有那些?
  • 响应中断的方法总结
    • Object.wait()/wait(long)/wait(long,int)
    • Thread.sleep(long)/sleep(long,int)
    • Thread.join()/join(long)/join(long,int)
    • juc.BlockingQueue.take()/put(E)
    • juc.Lock.lockInterruptibly()
    • juc.CountDownLatch.await()
    • juc.CyclicBarrier.await()
    • juc.Exchanger.exchange(V)
    • jio.InterruptibleChannel相关方法
    • jio.Selector相关方法
  • 那么,线程响应中断后应该怎么处理。

# 线程中断的最佳实践:

  • 传递中断
  • 不想或无法传递:恢复中断
  • 核心思想:不应屏蔽中断
  1. 传递中断:在方法签名中将中断异常抛出,而不是生吞,交给调用者处理。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 正确停止线程的方式1-抛出中断
 * 优先在方法签名中抛出该异常
 *
 * @author futao
 * @date 2020/6/6
 */
public class RightWayToStopThread implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("running...");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("响应中断,跳出循环,停止线程");
                break;
            }
        }
    }

    /**
     * 业务方法应该将中断异常抛出,将异常传递给上层--传递中断
     *
     * @throws InterruptedException
     */
    private void throwInMethod() throws InterruptedException {
        System.out.println("业务执行中.....");
        Thread.sleep(2000L);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayToStopThread());
        thread.start();
        Thread.sleep(1000L);
        thread.interrupt();
    }
}
  • 结果:
  1. 不想或无法传递时:应该恢复中断(Thread.currentThread().interrupt())
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 正确停止线程的方式2
 * 恢复中断
 *
 * @author futao
 * @date 2020/6/6
 */
public class RightWayToStopThreadReInterrupt implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println("running...");
            throwInMethod();
        }
        System.out.println("线程任务执行完毕");
    }

    private void throwInMethod() {
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            System.out.println("感知到中断请求。");
            System.out.println("重新设置中断信号");
            //尝试恢复中断
            Thread.currentThread().interrupt();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayToStopThreadReInterrupt());
        thread.start();
        Thread.sleep(1000L);
        thread.interrupt();
    }
}
  • 结果:

# 线程中断的相关方法

  • 预期下面代码的执行结果?
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 线程中断的相关方法
 *
 * @author futao
 * @date 2020/6/7
 */
public class InterruptMethod {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("线程任务执行中...");
            while (true) {
            }
        });

        //启动线程
        thread.start();
        System.out.println(thread.isInterrupted());
        //向线程发送中断信号
        thread.interrupt();
        System.out.println(thread.isInterrupted());
        System.out.println(thread.isInterrupted());
        System.out.println(Thread.interrupted());
        System.out.println(thread.isInterrupted());
        System.out.println(thread.interrupted());
        System.out.println(thread.isInterrupted());
    }
}
  • 结果:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
false
true
true
false
true
false
true
  • 分析:
    1. 线程处于运行状态,且没有程序给线程发送中断信号。所以非中断状态
    2. 调用了中断方法,所以线程状态状态为true
    3. 由于thread.isInterrupted()并不会清除线程的中断状态,所以多次调用,返回的结果一样,依旧为已中断
    4. Thread.interrupted()判断的是执行这行代码的线程的中断状态。这里是主线程,所以为未中断。且该方法调用之后,会将执行该方法的线程的中断状态清除。
    5. 因为Thread.interrupted()清除的是执行代码的线程的中断状态,所以不印象子线程的中断状态,所以子线程的中断状态仍然为true。
    6. 如果子线程对象直接调用静态方法interrupted(),返回的也是执行这段代码的线程的中断状态。此时为主线程,状态为未中断。
    7. 子线程对象直接调用静态方法interrupted()并不会清除调用对象的线程中断状态,而是清除执行这段代码的线程的中断状态。所以子线程的中断状态不影响。
  • 为什么通过子线程对象来执行静态方法static boolean interrupted()清除的是执行者的中断状态呢?查看源码发现,静态方法static boolean interrupted()会先获取到当前执行这段代码的线程,清除其中断状态,并返回中断状态。
  • 总结:
    1. thread.interrupt() 给线程发送中断信号,设置线程thread的中断状态为true。
    2. thread.isInterrupted() 判断线程thread是否被中断。且不改变线程的中断状态
    3. Thread.interrupted()/thread.interrupted() 判断执行这行代码的线程的中断状态,并且清除其中断状态。
    4. private native boolean isInterrupted(boolean ClearInterrupted); native方法,真正判断线程中断状态和清除中断状态的代码。thread.isInterrupted()Thread.interrupted()/thread.interrupted()最终调用的都是这个方法。
  • Q:如何清除线程的中断状态? 执行Thread.interrupted();这行代码的线程的中断状态会被清除。

# 哪些情况下线程会停止

  1. 线程run()方法正常执行完毕。(可借助线程中断机制提前结束run()方法)
  2. 线程发生了未捕获的异常。

# 错误的停止线程的方式

  • 被弃用的stop()suspend()resume()
  • 使用volatile设置boolean标记位的方式,不可靠

# 如何处理不可中断的阻塞

  • 并不是所有的阻塞都会响应中断,例如IO中的InputStream.read()。处理这类问题的方式要视情况而定,大概思路是手动编写程序检测线程的中断状态,如果线程被中断,则手动调用例如InputStream.close()方法来关闭流,实现停止线程。

# 本文源代码

https://github.com/FutaoSmile/learn-thread/tree/master/src/main/java/com/futao/learn/threads/c_%E5%A6%82%E4%BD%95%E5%81%9C%E6%AD%A2%E7%BA%BF%E7%A8%8B

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT牧场 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java中如何安全中断线程及其使用场景
在多线程编程中,线程中断是一种常见的控制线程执行流的机制,能够在一定程度上避免程序中线程因超时、死锁等原因而阻塞、浪费系统资源或造成程序卡死的问题。然而,直接停止线程的操作(如 Thread.stop() 或 Thread.suspend())是非常危险的,因为它们会带来不可预测的严重后果,比如线程持有的锁永远不会释放,导致其他线程永远无法获得锁而进入死锁状态。因此,Java 提供了一个较为优雅且安全的方式来中断线程,这就是通过使用中断信号来实现线程的安全终止。
九转成圣
2025/01/17
1510
如何停止一个正在运行的线程?
停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作。停止一个线程可以用Thread.stop()方法,但最好不要用它。
Java技术栈
2020/02/26
2K0
【Java 语言】Java 多线程 一 ( 线程基础 : 线程启动 | 线程停止 | 线程暂停 | 线程优先级 | 守护线程)
-- 1. 继承 Thread 运行线程 : 重写 Thread 类的 run 方法, 然后执行该线程;
韩曙亮
2023/03/27
3.2K0
Java多线程三:线程中断
有线程运行,肯定就会有线程中断,在Java语言中,线程中断是一种协作机制,通过对线程设置中断标记,告知对应的线程,根据中断标记来决定是否需要中断当前线程。也就是说,在线程运行过程中,其实我们没有办法安全,准确的终止一个线程。
全栈学习笔记
2022/06/14
5040
Java多线程三:线程中断
【JavaSE专栏82】线程中断,发送一个中断信号给另一个线程,让它中断当前的执行
本文讲解了 Java 中线程中断的语法和应用场景,并给出了样例代码。线程中断指的是一个线程发送一个中断信号给另一个线程,通知其应该中断当前的执行。
Designer 小郑
2023/08/21
6310
【JavaSE专栏82】线程中断,发送一个中断信号给另一个线程,让它中断当前的执行
Java多线程技能
本文介绍了线程的用法,包括线程的创建、启动、挂起、结束等操作,并讲解了线程的共享资源、线程的同步、线程的中断等概念。同时,本文还介绍了线程的优先级、线程的状态、线程的同步、线程的通信、线程的调度、线程的同步等问题。
Java后端工程师
2017/12/13
1.1K0
1.7停止线程
线程停止:在线程处理完任务之前,停掉正在做的操作,也就是放弃当前操作。 在java中有三种方法可以实现线程的停止: 使用退出标志,使线程正常退出,也就是当run方法执行完后线程终止。 使用stop强行
用户1134788
2017/12/27
1.9K0
1.7停止线程
JUC从实战到源码:中断机制与API实现
在Java中,线程中断是一种机制,用于通知线程应该停止当前正在执行的任务。中断通常用于协同线程之间的合作,以便让线程在适当的时候终止其工作,尤其是在长时间运行的任务或阻塞操作中。通过学了多线程以及synchronized的相关知识,接下来就到了学习线程中断知识。
怒放吧德德
2024/09/24
1591
每天一个Java面试题之interrupted和isInterrupted方法的区别
在Java编程中,多线程是一个重要的概念,它允许程序同时执行多个任务。在处理多线程时,线程中断是一个关键的机制,它允许一个线程通知另一个线程应该停止当前的操作。Java提供了interrupted和isInterrupted两个方法来处理线程中断,但它们之间有一些细微的差别。在这篇博客中,将深入探讨这两个方法的区别,并提供代码示例来帮助理解。
灬沙师弟
2024/09/10
1730
每天一个Java面试题之interrupted和isInterrupted方法的区别
Java多线程——多线程方法详解
从结果可知:Mythread的构造方法是被main线程调用的,而run方法是被名称为Thread-0的线程调用的,run方法是线程自动调用的
说故事的五公子
2019/11/26
9830
Java多线程的中断机制
这篇文章主要记录使用 interrupt() 方法中断线程,以及如何对InterruptedException进行处理。感觉对InterruptedException异常进行处理是一件谨慎且有技巧的活儿。
Vincent-yuan
2021/08/10
8730
宕机了,Redis 如何避免数据丢失?
停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作。停止一个线程可以用Thread.stop()方法,但最好不要用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是已被废弃的方法。在java中有以下3种方法可以终止正在运行的线程:
一行Java
2023/02/23
8970
宕机了,Redis 如何避免数据丢失?
《JavaSE-第二十一章》之线程的状态与中断
线程状态好比一个人的生命周期,从出生到死亡,期间会经历从婴儿到少年,从少年到青年,最终走向死亡。在java.lang.Thread.State枚举类中定义了以下六种线程的状态来描述线程的生命周期。
用户10517932
2023/10/07
2030
《JavaSE-第二十一章》之线程的状态与中断
浅聊线程中断
“ 在前面分析Condition的时候,被阻塞的线程在我关闭应用的时候,会抛出异常,这是因为阻塞的线程被其他线程中断了。其实在学习AQS的时候我们也说过线程中断,AQS中acquire方法会忽略线程中断。现在我们来了解一下什么叫线程中断”
每天学Java
2020/06/02
8540
关于interrupt(),interrupted(),isInterrupted()用法分析
interrupt()是用于中断线程的,调用该方法的线程的状态将被置为"中断"状态。注意:线程中断仅仅是设置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。这里可以看到中断后该线程还在继续往下执行,并没有强制终止线程。
砖业洋__
2023/05/06
3370
关于interrupt(),interrupted(),isInterrupted()用法分析
jdk1.8之线程中断
在Core Java中有这样一句话:"没有任何语言方面的需求要求一个被中断的程序应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断 " 线程中断不会使线程立即退出,而是给线程发送一个通知,告知目标线程有人希望你退出。至于目标线程接收到通知后如何处理,则完全由目标线程自行决定。
intsmaze-刘洋
2019/01/28
5590
如何正确的中断线程?你的姿势是否正确
在Java程序中,我们想要停止一个线程可以通过interrupt方法进行停止。但是当我们调用interrupt方法之后,它可能并不会立刻就会停止线程,而是通知线程需要停止。线程接收到通知之后会根据自身的情况判断是否需要停止,它可能会立即停止,也有可能会执行一段时间后停止,也可能根本就不停止。
一个程序员的成长
2022/12/18
7260
如何正确的中断线程?你的姿势是否正确
多线程专题---如何停止一个线程
最近做项目及看别人源码过程中涉及到多线程的知识,感觉自己多线程方面还不够系统,所以需要系统的总结一下这方面的知识。先从如何停止一个线程开始:
用户9854323
2022/06/25
6730
多线程专题---如何停止一个线程
Java并发之线程中断
     前面的几篇文章主要介绍了线程的一些最基本的概念,包括线程的间的冲突及其解决办法,以及线程间的协作机制。本篇主要来学习下Java中对线程中断机制的实现。在我们的程序中经常会有一些不达到目的不会
Single
2018/01/04
1.1K0
Java并发之线程中断
线程中断以及线程中断引发的那些问题,你值得了解
上周写了一篇多线程的文章,其实更多方面是偏基础一点的文章,而且也比较大白话,争取人人都能看的明白,再举一些常见的例子,能很好的帮助大家理解多线程,文章发表之后我投给了几个大号和CSDN反应都挺好的,大家表示希望能写更多这样的文章,希望再多写写线程相关的文章,所以我打算从线程的基础开始写起,每周写那么两三篇,大家闲的时候可以看看,也可以多吸收一点东西。
一个程序员的成长
2020/11/25
4870
线程中断以及线程中断引发的那些问题,你值得了解
推荐阅读
相关推荐
Java中如何安全中断线程及其使用场景
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验