[toc]

本系列参考资料《JAVA多线程编程核心技术》
关注公众号:雨中散步撒哈拉,回复:025
进行下载资料

一、前言

本节将讨论如何更好地停止一个线程。停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。虽然这看起来非常简单,但是必须做好防范措施,以便达到预期的效果。停止一个线程可以使用Thread.stop()方法,但最好不用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的( unsafe),而且是已被弃用作废的(deprecated),在将来的Java版本中,这个方法将不可用或不被支持。
大多数停止一个线程的操作使用Thread.interrupt()方法,尽管方法的名称是“停止,中止”的意思,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。

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

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
  2. 使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和 suspend及resume一样,都是作废过期的方法,使用它们可能产生不可预料的结果。
  3. 使用interrupt方法中断线程。

二、停不下来的多线程

1. 现象演示

使用interrupt方法中断线程。

自定义Thread类:

    @Override
    public void run() {
        for (int i = 0; i < 50000; i++) {
            System.out.println("i = " + (i + 1));
        }
    }

调用代码:

public static void main(String[] args) {
        try {
            MyStopThread myStopThread = new MyStopThread();
            myStopThread.start();
            // main线程休眠100毫秒
            Thread.sleep(100);
            // 标志停止
            myStopThread.interrupt();
            System.out.println("myStopThread------------------");
            System.out.println("isInterrupted===================");
            System.out.println("isInterrupted是否停止1=" + myStopThread.isInterrupted());
            System.out.println("isInterrupted是否停止2=" + myStopThread.isInterrupted());
            System.out.println("interrupted===================");
            System.out.println("myStopThread interrupted是否停止1=" + Thread.interrupted());
            System.out.println("myStopThread interrupted是否停止2=" + Thread.interrupted());


            Thread.currentThread().interrupt();
            System.out.println("Thread------------------");
            System.out.println("isInterrupted===================");
            System.out.println("isInterrupted是否停止1=" + Thread.currentThread().isInterrupted());
            System.out.println("isInterrupted是否停止2=" + Thread.currentThread().isInterrupted());
            System.out.println("interrupted===================");
            System.out.println("Thread interrupted是否停止1=" + Thread.interrupted());
            System.out.println("Thread interrupted是否停止2=" + Thread.interrupted());
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
        System.out.println("end");
    }

查看结果:
image.png

2. 判断是否结束

判断是否结束有俩个方法

  1. interrupted:是静态方法、内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态
  2. isInterrupted():是实例方法、是调用该方法的对象所表示的那个线程的isInterrupted(),不会重置当前线程的中断状态

上述现象打印结果分析:

  1. 结果1分析

image.png
myStopThread.isInterrupted()是否标记停止状态(执行者线程),由第一个红框可以知道,myStopThread已经被标记,isInterrupted()(没有状态清除功能)所以俩次都为true,myStopThread线程停止标记已经被标记!

image.png
Thread.interrupted():停止的是main线程(调用者线程),代码到目前没有操作main线程,所以返回为false

  1. 结果2分析

image.png
停止动作为停止main线程!
Thread.currentThread().isInterrupted()查看停止状态是否被标记上,结果为true。前边停止标记已经生效!

image.png
Thread.interrupted()获取是否标记停止状态
第一次为true:说明main线程已经被标记上!
第二次为false:说明main线程停止状态被重置

三、异常停止

1. 编写run代码

    @Override
    public void run() {
        try {
        for (int i = 0; i < 50000; i++) {
            if (this.isInterrupted()){
                System.out.println("已经是停止状态了!我要退出去!");
                    throw new InterruptedException();
            }
            System.out.println("i = " + (i + 1));
        }
        System.out.println("我被输出,如果此代码是for又继续循环,线程并未停止!");
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
    }

2. 编写main调用代码

    public static void main(String[] args) {
        try {
            MyStopThread myStopThread = new MyStopThread();
            myStopThread.start();
            // main线程休眠100毫秒
            Thread.sleep(100);
            myStopThread.interrupt();
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
        System.out.println("end");
    }

3. 执行并查看结果

image.png

四、暴力停止

1. 编写run代码

    @Override
    public void run() {
        for (int i = 0; i < 50000; i++) {
            System.out.println("i = " + (i + 1));
        }
        System.out.println("end!!!");
    }

2. 编写main调用代码

    public static void main(String[] args) {
        try {
            MyStopThread myStopThread = new MyStopThread();
            myStopThread.start();
            // main线程休眠100毫秒
            Thread.sleep(100);
            myStopThread.stop();
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
        System.out.println("end");
    }

3. 执行并查看结果

image.png

4. 可能产生的问题

方法 stop()已经被作废,因为如果强制让线程停止则有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。

五、return停止

1. 编写run代码

    @Override
    public void run() {
        for (int i = 0; i < 50000; i++) {
            if (this.isInterrupted()){
                System.out.println("已经是停止状态了!我要退出去!");
                return;
            }
            System.out.println("i = " + (i + 1));
        }
        System.out.println("我被输出,如果此代码是for又继续循环,线程并未停止!");
    }

2. 编写main调用代码

    public static void main(String[] args) {
        try {
            MyStopThread myStopThread = new MyStopThread();
            myStopThread.start();
            // main线程休眠100毫秒
            Thread.sleep(100);
            myStopThread.interrupt();
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
        System.out.println("end");
    }

3. 执行并查看结果

image.png

不过还是建议使用“抛异常”的方法来实现线程的停止,因为在catch 块中还可以将异常向上抛,使线程停止的事件得以传播。

六、睡眠中停止发生的现象

1. 编写run代码

    @Override
    public void run() {
        try {
            System.out.println("run begin");
            Thread.sleep(20000);
            System.out.println("run end");
        } catch (InterruptedException interruptedException) {
            System.out.println("在沉睡中被停止!进入catch!" + this.isInterrupted());
            interruptedException.printStackTrace();
        }
    }

2. 编写调用代码

public static void main(String[] args) {
        try {
            MyStopThread myStopThread = new MyStopThread();
            myStopThread.start();
            // main线程休眠100毫秒
            Thread.sleep(100);
            myStopThread.interrupt();
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
        System.out.println("end");
    }

3. 执行并查看结果

image.png

进入catch,并抛出异常,并清除停止状态值,变为false

Q.E.D.


只有创造,才是真正的享受,只有拚搏,才是充实的生活。