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

文章目录

  • 概述 stop() Deprecated
  • 方式一 设置开关
  • 方式二 调用interrupt API
  • 方式三 暴力结束线程-> Daemon Thread + interrupt API

在这里插入图片描述


概述 stop() Deprecated

通过阅读源码或者官方的API,可以知道 Thread#stop() 方法已经被废弃了。
高并发编程-Thread_正确关闭线程的三种方式_第1张图片

大致意思

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

使用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);
        }

    }

}


高并发编程-Thread_正确关闭线程的三种方式_第2张图片

运行结果:

高并发编程-Thread_正确关闭线程的三种方式_第3张图片


方式二 调用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
            }
        }
    }

}


高并发编程-Thread_正确关闭线程的三种方式_第4张图片

运行结果:

高并发编程-Thread_正确关闭线程的三种方式_第5张图片


方式三 暴力结束线程-> 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);

    }


}




执行结果:

高并发编程-Thread_正确关闭线程的三种方式_第6张图片

你可能感兴趣的:(【Java并发编程】)