• 如何停止一个线程?


    停止线程在java语言中并不像break语句那样干脆,还需要一些技巧性的处理。

    停止一个线程意味着在线程处理完任务之前停止正在做的操作,也就是放弃当前的操作,虽然这看起来非常简单,但是必须做好防范措施,以便达到预期的效果。

    停止一个线程可以使用Thread.stop()方法,但不推荐使用此方法,虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是被弃用作废的。

    大多数情况下,停止一个线程使用Thread.interrupt()方法,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。

    在java中有3种方法可以使正在运行的线程终止运行:

    • 使用退出标志使线程正常退出。
    • 使用stop()方法强行终止线程,但是这个方法不推荐使用,因为stop()和suspend()、resume()一样,都是作废过期的方法,使用它们可能发生不可预料的结果。
    • 使用interrupt()方法中断线程。

    停止不了的线程

    本示例将调用interrupt()方法来停止线程,但interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt()方法仅仅是在当前线程中做了一个停止的标记,并不是真正停止线程。

    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 500000; i++) {
                System.out.println("i=" + (i + 1));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(2000);
            thread.interrupt();
            System.out.println("zzzzzzzzzzzzzzzzzz");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    调用interrupt()方法并没有将线程停止,那如何停止线程呢?

    判断线程是否为停止状态

    interrupted()方法

    Thread.java类提供了两个判断方法来判断线程的状态是否是停止的:

    • public static boolean interrupted():测试currentThread()是否已经中断。
    • public boolean this.isInterrupted():测试this关键字所在类的对象是否已经中断。

    那这两个方法有什么区别呢?先来看方法this.isInterrupted()方法的解释:测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程。如下代码所示:

    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 5000; i++) {
                System.out.println("i=" + (i + 1));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(10);
            thread.interrupt();
            System.out.println("是否停止1?="+thread.interrupted());
            System.out.println("是否停止2?="+thread.interrupted());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    output:
    i=517
    i=518
    i=519
    i=520
    i=521
    是否停止1?=false
    i=522
    是否停止2?=false
    i=523
    i=524
    i=525
    i=526
    i=527
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    虽然通过在thread对象上调用代码:

    thread.interrupt()

    然后又使用代码:

    thread.interrupted()

    判断线程是否中断,返回false,因为interrupted()方法返回当前线程是否中断,当前线程是main线程,从未中断过,所以返回false。

    最好使用Thread.interrupted(),因为interrupted()为静态方法,大多数是针对currentThread()线程进行操作的。

    public class Run2 {
        public static void main(String[] args) {
            Thread.currentThread().interrupt();
            System.out.println("是否停止1?=" + Thread.interrupted());
            System.out.println("是否停止2?=" + Thread.interrupted());
            System.out.println("end!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    output:
    是否停止1?=true
    是否停止2?=false
    end!
    
    • 1
    • 2
    • 3
    • 4

    为啥第一个interrupted()方法判断了当前线程是否停止状态,第二个却返回false呢?

    interrupted()方法在官方文档的解释为:

    判断当前线程是否已经中断。线程的中断状态由该方法清除。

    isInterrupted()方法

    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(10);
            thread.interrupt();
            System.out.println("是否停止1?="+thread.isInterrupted());
            System.out.println("是否停止2?="+thread.isInterrupted());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    output:
    i=542
    i=543
    i=544
    i=545
    i=546
    i=547
    是否停止1?=true
    i=548
    是否停止2?=true
    i=549
    i=550
    i=551
    i=552
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    从结果中可以看出,isInterrupted()方法并未清除状态标志,不具有此功能,所以输出两个true。

    综上:

    • interrupted():测试当前线程是否已经是中断状态,执行后具有清除状态标志值的功能。
    • isInterrupted():测试线程Thread对象是否已经是中断状态,不清除状态标志。

    能停止的线程-异常法

    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 5000; i++) {
                if(this.isInterrupted()){
                    System.out.println("已经是停止状态了!我要退出了!");
                    break;
                }
                System.out.println("i=" + (i + 1));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(10);
            thread.interrupt();
            System.out.println("end");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    output:
    i=59
    i=60
    i=61
    i=62
    i=63
    i=64
    i=65
    i=66
    i=67
    i=68
    i=69
    i=70
    i=71
    i=72
    i=73
    已经是停止状态了!我要退出了!
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    上面代码虽然停止了线程,但如果for语句下面还有语句,那么程序还是会继续运行。

    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 5000; i++) {
                if(this.isInterrupted()){
                    System.out.println("已经是停止状态了!我要退出了!");
                    break;
                }
                System.out.println("i=" + (i + 1));
            }
            System.out.println("我被输出,线程并未停止");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    output:
    i=32
    i=33
    i=34
    i=35
    i=36
    i=37
    i=38
    i=39
    i=40
    i=41
    i=42
    end
    已经是停止状态了!我要退出了!
    我被输出,线程并未停止
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    如何解决语句继续执行的问题呢?使用异常,如下代码所示:

    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                for (int i = 0; i < 5000; i++) {
                    if(this.isInterrupted()){
                        System.out.println("已经是停止状态了!我要退出了!");
                        throw new InterruptedException();
                    }
                    System.out.println("i=" + (i + 1));
                }
                System.out.println("我被输出,线程并未停止");
            }catch (InterruptedException e){
                System.out.println("线程中断,抛出异常,被捕获");
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    output:
    i=94
    i=95
    i=96
    i=97
    java.lang.InterruptedException
    i=98
    end
    已经是停止状态了!我要退出了!
    	at com.example.threadcore.MyThread.run(MyThread.java:16)
    线程中断,抛出异常,被捕获
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    由程序运行结果可以看出,线程终于被正确停止了。这就是使用interrupt()方法中断线程。

    在sleep状态下停止线程

    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                System.out.println("run begin");
                Thread.sleep(200000);
                System.out.println("run end");
            } catch (InterruptedException e) {
                System.out.println("在沉睡中被停止,进入catch" + this.isInterrupted());
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(10);
            thread.interrupt();
            System.out.println("end");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    output:
    run begin
    end
    在沉睡中被停止,进入catch false
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at com.example.threadcore.MyThread.run(MyThread.java:14)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    从运行结果来看,如果线程在sleep状态下停止,则该线程会进入catch语句,并且清除停止状态值,变成false。

    以上示例是先调用sleep()方法,再调用interrupt()方法停止线程,还有一个反操作,即先调用interrupt()方法,再调用sleep()方法,这种情况下也会出现异常。

    public class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                for (int i = 0; i < 10000; i++) {
                    System.out.println("i= " + (i + 1));
                }
                System.out.println("run begin");
                Thread.sleep(200000);
                System.out.println("run end");
            } catch (InterruptedException e) {
                System.out.println("先停止,再遇到了sleep,进入catch " + this.isInterrupted());
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    public class Run {
        public static void main(String[] args) throws InterruptedException {
            MyThread thread = new MyThread();
            thread.start();
            thread.interrupt();
            System.out.println("end");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    output:
    i= 9994
    i= 9995
    i= 9996
    i= 9997
    i= 9998
    i= 9999
    i= 10000
    run begin
    先停止,再遇到了sleep,进入catch false
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at com.example.threadcore.MyThread.run(MyThread.java:17)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    综上,不管调用的顺序,只要interrupt()和sleep()方法碰到一起就会出现异常:

    • 在sleep状态执行interrupt()方法会出现异常。
    • 调用interrupt()方法给线程打了中断标记,再执行sleep()方法也会出现异常。

    使用return语句停止线程的缺点和解决方案

    虽然使用return较“抛异常”法在代码结构上可以更加方便地实现线程的停止,不过还是建议使用“抛异常”法,因为在catch块中可以对异常的信息进行统一的处理。

  • 相关阅读:
    git如何删除github上的文件,亲测有效
    ArcGIS 10.7之 栅格影像裁剪操作
    spring使用模板模式
    智能生活 App SDK 开发入门教程【内附代码段 】
    Redis 持久化之AOF操作
    正则表达式——3.更多种搜寻比对模式
    聊聊流式数据湖Paimon(五)
    Redis系列——redis启动,客户端day1-2
    第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南)-L Bit Sequence
    【vue3】:Excel导入导出解决方案
  • 原文地址:https://blog.csdn.net/wozaibohaibian/article/details/125469850