高并发编程-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


相关文章
|
4天前
|
存储 缓存 安全
【并发编程】线程池以及场景题
【并发编程】线程池以及场景题
16 0
|
4天前
|
存储 监控 安全
【并发编程】线程安全(下)
【并发编程】线程安全
7 0
|
4天前
|
存储 安全 Java
【并发编程】线程安全(上)
【并发编程】线程安全
11 0
|
4天前
|
Java 程序员 调度
【并发编程】线程基础知识
【并发编程】线程基础知识
9 0
|
1天前
|
Java
深入理解Java并发编程:线程池的应用与优化
【5月更文挑战第18天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将了解线程池的基本概念,应用场景,以及如何优化线程池的性能。通过实例分析,我们将看到线程池如何提高系统性能,减少资源消耗,并提高系统的响应速度。
11 5
|
1天前
|
消息中间件 安全 Java
理解Java中的多线程编程
【5月更文挑战第18天】本文介绍了Java中的多线程编程,包括线程和多线程的基本概念。Java通过继承Thread类或实现Runnable接口来创建线程,此外还支持使用线程池(如ExecutorService和Executors)进行更高效的管理。多线程编程需要注意线程安全、性能优化和线程间通信,以避免数据竞争、死锁等问题,并确保程序高效运行。
|
1天前
|
安全 Java 容器
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第18天】随着多核处理器的普及,并发编程变得越来越重要。Java提供了丰富的并发编程工具,如synchronized关键字、显式锁Lock、原子类、并发容器等。本文将深入探讨Java并发编程的核心概念,包括线程安全、死锁、资源竞争等,并分享一些性能优化的技巧。
|
1天前
|
安全 Java 开发者
Java中的多线程编程:理解与实践
【5月更文挑战第18天】在现代软件开发中,多线程编程是提高程序性能和响应速度的重要手段。Java作为一种广泛使用的编程语言,其内置的多线程支持使得开发者能够轻松地实现并行处理。本文将深入探讨Java多线程的基本概念、实现方式以及常见的并发问题,并通过实例代码演示如何高效地使用多线程技术。通过阅读本文,读者将对Java多线程编程有一个全面的认识,并能够在实际开发中灵活运用。
|
3天前
|
存储 关系型数据库 MySQL
《MySQL 入门教程》第 05 篇 账户和权限,Java高并发编程详解深入理解pdf
《MySQL 入门教程》第 05 篇 账户和权限,Java高并发编程详解深入理解pdf
|
3天前
|
数据处理 Python
Python并发编程:实现高效的多线程与多进程
Python作为一种高级编程语言,提供了强大的并发编程能力,通过多线程和多进程技术,可以实现程序的并发执行,提升系统的性能和响应速度。本文将介绍Python中多线程和多进程的基本概念,以及如何利用它们实现高效的并发编程,解决实际开发中的并发性问题。

热门文章

最新文章