高并发编程-Thread_正确关闭线程的三种方式

简介: 高并发编程-Thread_正确关闭线程的三种方式

20191031000606569.png


概述 stop() Deprecated

通过阅读源码或者官方的API,可以知道 Thread#stop() 方法已经被废弃了。


20190922215854847.png

大致意思


这种方法本质上是不安全的。


使用Thread.stop停止线程会导致它解锁所有已锁定的监视

如果先前由这些监视器保护的任何对象处于不一致状态,则损坏的对象将对其他线程可见,从而可能导致任意行为。


stop的许多用法应由仅修改某些变量以指示目标线程应停止运行的代码代替。

目标线程应定期检查此变量,如果该变量指示要停止运行,则应按有序方式从其运行方法返回。


如果目标线程等待很长时间(例如,在条件变量上),则应使用中断方法来中断等待


详见: —> Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.


那该如何正确的终止线程呢? 这里给出几个思路及demo,供大家参考


方式一 设置开关

package com.artisan.test;
public class StopThread_1 {
    public static void main(String[] args) {
        WorkThread workThread = new WorkThread();
        workThread.start();
        // main线程继续执行业务逻辑  假设运行了3秒
        try {
            System.out.println(Thread.currentThread().getName() + " 运行中");
            Thread.sleep(3_000);
            // 假设触发某个条件,需要退出WorkThread线程
            workThread.shutdownThread();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    static class WorkThread extends Thread {
        // 线程内部设置开关 volatile 多线程可见
        private volatile boolean flag = true;
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " working , flag=" + flag);
            // 通过接收flag来决定 终止或运行
            while (flag) {
                // do something ......
            }
        }
        private void shutdownThread() {
            this.flag = false;
            System.out.println(Thread.currentThread().getName() + " set flag=" + flag);
        }
    }
}


image.png


运行结果:


20190923000550189.png

方式二 调用interrupt API

package com.artisan.test;
public class StopThread_2 {
    public static void main(String[] args) {
        WorkThread workThread = new WorkThread("workThread");
        workThread.start();
        try {
            // 模拟主线程的业务
            System.out.println(Thread.currentThread().getName() + " working...");
            Thread.sleep(3_000);
            // 假设触发了某种条件,需要中断workThread线程的执行 调用interrupt
            workThread.interrupt();
            System.out.println("workThread interrupt...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    static class WorkThread extends Thread {
        public WorkThread(String name) {
            super(name);
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " working ");
            // 死循环
            while (true) {
                // 判断 该线程是否被打断
                if (Thread.interrupted()) {
                    System.out.println(Thread.currentThread().getName() + " received  interrupt signal...");
                    // break (break的话  还会执行 assume some logic is here的代码)
                    // 或者
                    // return (return 的话,如果有后面还有代码的话就不会执行后续的代码了)
                    break;
                }
                // assume some logic is here
            }
        }
    }
}


image.png


运行结果:

image.png


方式三 暴力结束线程-> Daemon Thread + interrupt API


我们在前面使用了

高并发编程-Daemon Thread的创建以及使用场景分析

高并发编程-Thread#interrupt用法及源码分析


在Daemon Thread中我们知道: UserThread 结束后, 那在UserThread中设置的Daemon Thread ,JVM不关心守护程序线程是否正在运行,也就是说即使是Daemon Thread 还在运行,只要UserThread结束了,那么Daemon Thread 就一定会退出,这是由JVM保障的。


那提个问题:


1:那我们是不是可以把业务线程设置成Daemon Thread 呢?


2: 假设可以的话,那哪个线程要和Daemon Thread 绑定在一起呢?


3: 和Daemon Thread 绑定在一起该如何结束呢?


针对问题1 —> 可以


针对问题2 —>实例化一个用于创建UserThread的类,用于创建UserThread执行线程. 在这个UserThread执行线程中,实例化一个线程出来,并设置该线程为Daemon Thread,用于执行业务逻辑


针对问题3 —> 这里我们可以借用interrupt的方式来终止和Daemon Thread 绑定在一起的User Thread.


package com.artisan.test;
public class ThreadControl {
    //执行线程
    private Thread executeThread;
    // 内存可见的标识符号
    private volatile boolean finished = false;
    public void execute(Runnable task) {
        // Step1 创建执行线程 并启动
        executeThread = new Thread(() -> {
            // Step2 创建守护线程 用于执行任务
            Thread runner = new Thread(task);
            runner.setDaemon(true);
            // 启动守护线程执行任务(当外层的执行线程结束的时候,JVM会确保将该守护线程也一并关闭)
            runner.start();
            try {
                // join到当前线程,该任务完成后,才继续后续的代码,如果未执行完会一直阻塞在这里
                runner.join();
                // runner执行完以后,设置finished为true
                finished = true;
            } catch (InterruptedException e) {
                //e.printStackTrace();
            }
        });
        // 启动执行线程
        executeThread.start();
        System.out.println("任务开始执行...");
    }
    /**
     *  该shutdown方法,由创建ThreadControl实例的线程调用
     * @param mills 最大执行时间
     */
    public void shutdown(long mills) {
        long shutdownTime = System.currentTimeMillis();
        // 如果任务没有执行完...
        while (!finished) {
            //任务超时。即在规定的最大执行时间内未完成,终止该任务
            if (System.currentTimeMillis() - shutdownTime >= mills){
                // 调用interrupt方法 ,退出当前执行线程
                executeThread.interrupt();
                System.out.println("任务超时,interrupt该任务!");
                break;
            }
            // 如果没有超时,休眠1毫秒 ,然后再继续进到while循环判断
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                System.out.println("执行线程被打断");
                break;
            }
        }
        // 恢复初始状态
        finished = false;
    }
    public static void main(String[] args) {
        // 测试一 : 任务在规定的最大存活时间内未执行完成
        long start = System.currentTimeMillis();
        ThreadControl ts = new ThreadControl();
        ts.execute(() -> {
            while (true) {
                //假设死循环,一直运行
            }
        });
        // 最长执行10秒,超过10秒,中断该线程
        ts.shutdown(10_000);
        long end = System.currentTimeMillis();
        System.out.printf("任务被终止,共耗时【%s】", end - start);
        System.out.println("=====================================");
        // 测试二 : 任务执行时间小于规定的最大存活时间
        start = System.currentTimeMillis();
        ThreadControl tc = new ThreadControl();
        // 模拟该任务 5秒执行完成
        tc.execute(() -> {
            try {
                Thread.sleep(5_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        // 最大允许存活10秒
        tc.shutdown(10_000);
        end = System.currentTimeMillis();
        System.out.printf("任务已结束,共耗时【%s】", end - start);
    }
}

执行结果:

20190923235557468.png


相关文章
|
2月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
152 6
|
5月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
306 83
|
5月前
|
人工智能 安全 调度
Python并发编程之线程同步详解
并发编程在Python中至关重要,线程同步确保多线程程序正确运行。本文详解线程同步机制,包括互斥锁、信号量、事件、条件变量和队列,探讨全局解释器锁(GIL)的影响及解决线程同步问题的最佳实践,如避免全局变量、使用线程安全数据结构、精细化锁的使用等。通过示例代码帮助开发者理解并提升多线程程序的性能与可靠性。
189 0
|
7月前
|
Java 开发者 Kotlin
华为仓颉语言初识:并发编程之线程的基本使用
本文详细介绍了仓颉语言中线程的基本使用,包括线程创建(通过`spawn`关键字)、线程名称设置、线程执行控制(使用`get`方法阻塞主线程以获取子线程结果)以及线程取消(通过`cancel()`方法)。文章还指出仓颉线程与Java等语言的差异,例如默认不提供线程名称。掌握这些内容有助于开发者高效处理并发任务,提升程序性能。
247 2
|
2月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
271 0
|
3月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
239 16
|
3月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
|
7月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
259 0
|
7月前
|
Arthas 监控 Java
Arthas thread(查看当前JVM的线程堆栈信息)
Arthas thread(查看当前JVM的线程堆栈信息)
1223 10
|
10月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
189 26

热门文章

最新文章