Java 正确终止线程的方法

 更新时间:2020年12月02日 10:12:05   作者:yuanyb  
这篇文章主要介绍了Java 正确终止线程的方法,帮助大家更好的理解和学习java 多线程的相关知识,感兴趣的朋友可以了解下

Thread类中有一个已经废弃的 stop() 方法,它可以终止线程,但由于它不管三七二十一,直接终止线程,所以被废弃了。比如,当线程被停止后还需要进行一些善后操作(如,关闭外部资源),使用这个方法就无能为力了。可以通过线程中断来实现线程终止。

首先来看一下Java线程中断的一些内容:

  • Java平台为每个线程维护了一个布尔型的中断标记,可以通过下列方法获取该标记的值:

            interrupt() 中断某个线程
            isInterrupted() 返回该线程的中断标记
            interrupted() 返回并重置该线程的中断标记(置为false)

  • 中断仅是发起线程对目标线程的一种请求,也就是说,目标线程对这种请求可以相应,也可以忽略。
  • Java标准库中与线程阻塞相关的方法对中断的相应方式都是抛出 InterruptedException 异常,并且按照惯例,抛出异常前都会重置中断标记为false,因此这些方法会清空线程的中断标记。
  • Java标准库中与线程阻塞相关的方法在进行阻塞前会判断中断标记是否为true,为true则抛出异常;如果在阻塞后调用中断方法的话,那么JVM会设置该线程的中断标记,然后将该线程唤醒,因此中断具有唤醒线程的作用。

由上面几点和第二句加粗的话可知,可以使用线程中断来实现线程终止,只要目标线程判断一下中断标记即可,即使被中断的线程正处于阻塞状态,也能把他唤醒起来终止;由第一句加粗的话可知,直接使用线程中断实现线程终止是存在风险的,因为可能调用了一些Java标准库的阻塞方法,而导致了中断标记被清空,也就无法获得中断标记了(总是false),因此需要自己创建一个中断标记配合使用。

如,下面是一个可中断的任务执行器,他会在每次执行任务前,判断一下自定i的终止标记和剩余的任务数(善后);提供的shutdown方法除了将工作线程中断外(主要作用是唤醒可能处于阻塞状态的任务),还会将终止交集 terminated 置为 true。

执行 main 方法,可以发现,首先会打印出“客户端调用了 shutdown 方法”,然后过了四秒,main线程才会终止,可知shutdown方法正确地将目标线程终止了。关于“按照惯例,Java标准库中抛出InterruptedException异常的和线程相关的阻塞方法会清空中断标记”,可以将条件中的 !interminated 替换成 !Thread.currentThread().isInterrupted(),然后再执行main方法测试,可以发现main线程始终无法终止,因为 sleep() 方法清空了中断标记,所以  !Thread.currentThread().isInterrupted() 始终为true,导致工作线程始终无法终止。

public class TerminableTaskRunner {
    // 存储要执行的任务
    private final BlockingQueue<Runnable> tasks;
    // 线程终止标志
    private volatile boolean terminated;
    // 剩余的任务数
    private final AtomicInteger count;
    // 实际执行任务的线程
    private volatile Thread workThread;
 
    public TerminableTaskRunner(int capacity) {
        this.tasks = new LinkedBlockingDeque<>(capacity);
        this.count = new AtomicInteger(0);
        this.workThread = new WorkThread();
        workThread.start();
    }
 
    public void submit(Runnable task) {
        this.tasks.add(task);
        this.count.incrementAndGet();
    }
 
    public void shutdown() {
        terminated = true; // 线程终止标志,由于中断标志可能会被覆盖,所以需要自己创建一个标志
        if (workThread != null)
            workThread.interrupt(); // 唤醒线程
    }
 
    private class WorkThread extends Thread {
        @Override
        public void run() {
            Runnable task;
            try {
                while (!terminated || tasks.size() >= 1) {
                    task = tasks.take();
                    try {
                        task.run(); // 可能会清空当前线程的中断标记,如task.run()在内部调用的阻塞方法抛出了InterruptedException
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                    count.decrementAndGet();
                }
            } catch (InterruptedException e) {
                // 一旦调用shutdown且tasks.take()阻塞住,就抛出该异常,没有任务要执行,直接终止
                workThread = null;
            }
        }
    }
 
    public static void main(String[] args) {
        TerminableTaskRunner taskRunner = new TerminableTaskRunner(4);
        for (int i = 0; i < 4; i++) {
            taskRunner.submit(()->{
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    System.out.println("客户端调用了 shutdown 方法");
                }
            });
        }
        taskRunner.shutdown();
 
    }
}

以上就是Java 正确终止线程的方法的详细内容,更多关于Java 终止线程的资料请关注脚本之家其它相关文章!

相关文章

  • 基于HTML5+js+Java实现单文件文件上传到服务器功能

    基于HTML5+js+Java实现单文件文件上传到服务器功能

    应公司要求,在HTML5页面上实现上传文件到服务器功能,对于我这样的菜鸟,真是把我难住了,最后还是请教大神搞定的,下面小编把例子分享到脚本之家平台,供大家参考
    2017-08-08
  • springboot整合通用Mapper简化单表操作详解

    springboot整合通用Mapper简化单表操作详解

    这篇文章主要介绍了springboot整合通用Mapper简化单表操作,通用Mapper是一个基于Mybatis,将单表的增删改查通过通用方法实现,来减少SQL编写的开源框架,需要的朋友可以参考下
    2019-06-06
  • Java设计模式中的外观模式详解

    Java设计模式中的外观模式详解

    外观模式为多个复杂的子系统,提供了一个一致的界面,使得调用端只和这个接口发生调用,而无须关系这个子系统内部的细节。本文将通过示例详细为大家讲解一下外观模式,需要的可以参考一下
    2023-02-02
  • SpringBoot文件上传(本地存储)回显前端操作方法

    SpringBoot文件上传(本地存储)回显前端操作方法

    这篇文章主要介绍了SpringBoot文件上传(本地存储)回显前端操作方法的相关资料,文中讲解了文件上传的基本原理,包括前端调用后端接口上传文件,后端返回文件路径给前端,前端通过路径访问图片,需要的朋友可以参考下
    2024-11-11
  • Java跳台阶实现思路和代码

    Java跳台阶实现思路和代码

    今天小编就为大家分享一篇关于Java跳台阶实现思路和代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Spring事务aftercommit原理及实践

    Spring事务aftercommit原理及实践

    这篇文章主要为大家介绍了Spring事务aftercommit原理及实践,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Java后端学习精华之TCP通信传输协议详解

    Java后端学习精华之TCP通信传输协议详解

    TCP/IP是一种面向连接的、可靠的、基于字节流的传输层通信协议,它会保证数据不丢包、不乱序。TCP全名是Transmission Control Protocol,它是位于网络OSI模型中的第四层
    2021-09-09
  • Java中关于Collections集合工具类的详细介绍

    Java中关于Collections集合工具类的详细介绍

    Java提供了一个操作Set、List和Map等集合的工具类:Collections,该工具提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了将集合对象设置为不可变、对集合对象实现同步控制等方法
    2021-09-09
  • jvm中指定时区信息user.timezone问题及解决方式

    jvm中指定时区信息user.timezone问题及解决方式

    同一份程序使用时间LocalDateTime类型,在国内和国外部署后,返回的时间信息前端使用出问题,这篇文章主要介绍了jvm中指定时区信息user.timezone问题及解决方法,需要的朋友可以参考下
    2023-02-02
  • 在mybatis中去除多余的前缀或者后缀操作

    在mybatis中去除多余的前缀或者后缀操作

    这篇文章主要介绍了在mybatis中去除多余的前缀或者后缀操作。具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11

最新评论