• 线程的“结束”


    【一道概率很高的面试题】:

    • 如何优雅的结束一个线程?
      上传一个大文件,正在处理费时的计算,如何优雅的结束这个线程呢?

    【stop方法】:

    【为何不建议使用stop呢?】:
    因为很容易产生数据不一致的问题;
    stop方法不管你线程处于什么状态,如果持有很多把锁,它会二话不说释放所有的锁,并且不会做善后的工作。原先的锁如果是为了数据的一致性,那么使用stop有可能就会打破数据的一致性。

     public class T01_Stop {
        public static void main(String[] args) {
            Thread t = new Thread(() -> {
                while (true) {
                    System.out.println("go on");
                    SleepHelper.sleepSeconds(1);
                }
            });
    
            t.start();
    
            SleepHelper.sleepSeconds(5);
    
            t.stop();  //不建议大家使用。
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    【 suspend_resume 】:

    【这两个方法被废掉的原因】:
    原因和stop非常的类似 , 万一暂停的瞬间持有一把锁 , 这一把锁是不会被释放的,如果你忘记了恢复,那么就缔造出了一把《永恒的锁》。所以也是会产生数据不一致的问题的。

    public class T02_Suspend_Resume {
        public static void main(String[] args) {
            Thread t = new Thread(() -> {
                while (true) {
                    System.out.println("go on");
                    SleepHelper.sleepSeconds(1);
                }
            });
    
            t.start();
    
            SleepHelper.sleepSeconds(5);
    
            t.suspend();  //暂停;
            SleepHelper.sleepSeconds(3);
            t.resume();  //恢复执行。
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    volatile结束线程】:

    【不太适合的情况】:
    循环中有个数统计的计数器。
    循环中有wait、recv、accept等方法,wait会阻塞住,不能进行下一次循环,也无法结束。
    【适合的情况】:
    不太依赖中间的状态——如上传数据,多上传一些 / 少上传一些 都不影响的这种情况。

    public class T03_VolatileFlag {
    
        private static volatile boolean running = true;
    
        public static void main(String[] args) {
            Thread t = new Thread(() -> {
                long i = 0L;
                while (running) {
                    //wait recv accept
                    i++;
                }
    
                System.out.println("end and i = " + i); //4168806262 4163032200
            });
    
            t.start();
    
            SleepHelper.sleepSeconds(1);
    
            running = false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    【interrupt结束线程】:

    //interrupt比volatile更加优雅一些 , 因为如果是volatile的话 , 中间有一些wait / sleep 的话 , 你是停不了的 , 无法跳到下一次循环。但如果是interrupt的话,你只要在sleep / wait 里面处理 InterruptedException 也可以正确的结束这个线程。
    【仍有缺点】:
    10个元素的容器 , 精确到第5个 , 这个也是无法达到的!!!
    【这种情况如何正常结束呢?】:
    必须生产者Thread和外面打断它的Thread , 两者配合才可以。
    【其他方法】:
    内部线程进行定时的检查 , 每隔多长时间检查一次标志位 , 每经过一次循环检查一次标志位。

    /**
     * interrupt是设定标志位
     */
    public class T04_Interrupt_and_NormalThread {
        public static void main(String[] args) {
            Thread t = new Thread(() -> {
                while (!Thread.interrupted()) {   //只要标记位没被设置,我就一直循环~ ~ ~
                    //sleep wait
                }
    
                System.out.println("t1 end!");
            });
    
            t.start();
    
            SleepHelper.sleepSeconds(3);
    
            t.interrupt();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    public class T05_InterruptAndPark {
        public static void main(String[] args) {
    
            Thread t = new Thread(() -> {
                System.out.println("1");
                LockSupport.park();
                System.out.println("2");
            });
    
            t.start();
    
            SleepHelper.sleepSeconds(2);
    
            t.interrupt();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    【阶段总结】:

    【volatile 和 interrupt】:
    总之,你不依赖于中间精确的次数和精确的时间的话 , 它俩都行。

    【结束线程的方法】:

    1. 自然结束(能自然结束就尽量自然结束)
    2. stop() suspend() resume()
    3. volatile标志
      1. 不适合某些场景(比如还没有同步的时候,线程做了阻塞操作,没有办法循环回去)
      2. 打断时间也不是特别精确,比如一个阻塞容器,容量为5的时候结束生产者,
        但是,由于volatile同步线程标志位的时间控制不是很精确,有可能生产者还继续生产一段儿时间
    4. interrupt() and isInterrupted(比较优雅)
  • 相关阅读:
    SpringBoot-----从前端更新数据到MySql数据库
    vue制作npm插件并上传
    Springboot学生作业管理系统毕业设计-附源码251208
    python-批量操作excel
    [Python私活案例]24行代码,轻松赚取400元,运用Selenium爬取39万条数据
    C++基础知识(二十五)--- STL案例之演讲比赛
    Python爱好者的自我修养(1):简单输入与输出
    YRX app第八题 python grpc
    TCGA里面的任意基因做生存分析 批量生存分析
    Spring Boot Mock
  • 原文地址:https://blog.csdn.net/fuyuanduan/article/details/127868989