多线程系列之优雅停止正在运行的线程【十】

停止一个线程意味着任务没有处理完之前放弃当前的操作。停止一个线程可以用Thread.stop()方法,但最好不要用它,因为这个方法是不安全的,而且已经被废弃。

写在前面:推荐使用异常来实现线程的停止,因为在catch块中还可以将异常向上抛,使线程停止事件得以传播。

1. 停止不了的线程

interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt方法是在当前线程中打了一个停止标志,并不是真的停止线程。

package com.linyf.demo.thread.stop;

public class StopThreadTest1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
        try {
            Thread.sleep(500);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyThread extends Thread {
    public void run(){
        for(int i=0; i<500000; i++){
            System.out.println("i="+(i+1));
        }
    }
}

执行结果:
多线程系列之优雅停止正在运行的线程【十】_第1张图片

2. 判断线程是否停止状态

Thread类中有两种方法:

  • this.interrupted(): 测试当前线程是否已经中断;
  • this.isInterrupted(): 测试线程是否已经中断;

这两个方法的区别:

2.1 interrupted()方法

测试当前线程是否已经中断,当前线程指运行this.interrupted()方法的线程。

package com.linyf.demo.thread.stop;

public class StopTest1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        new Thread(() -> thread.run()).start();
        try{
            Thread.sleep(2000);
            thread.interrupt();

            System.out.println("stop 1 interrupt:" + thread.interrupted());
            System.out.println("stop 2 interrupt:" + thread.interrupted());
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for(int i=0; i<500000; i++){
            i++;
        }
    }
}

运行结果:
多线程系列之优雅停止正在运行的线程【十】_第2张图片
StopTest1 中虽然是在thread对象上调用:thread.interrupt(), 后面又使用在这里插入图片描述
来判断thread对象所代表的线程是否停止,但从控制台打印的结果来看,线程并未停止,这也证明了interrupted()方法的解释,测试当前线程是否已经中断。

这个当前线程是main,它从未中断过,所以打印的结果是两个false.

使main线程产生中断效果:

package com.linyf.demo.thread.stop;

public class StopTest2 {
    public static void main(String[] args) {
        Thread.currentThread().interrupt();
        System.out.println("stop 1 interrupt:" + Thread.currentThread().interrupted());
        System.out.println("stop 2 interrupt:" + Thread.currentThread().interrupted());
    }
}

执行结果:
多线程系列之优雅停止正在运行的线程【十】_第3张图片
方法interrupted()的确判断出当前线程是否是停止状态。但为什么第2个布尔值是false呢?

官方帮助文档中对interrupted方法的解释:

测试当前线程是否已经中断。线程的中断状态由该方法清除。也就是说,如果连续两次调用该方法,则第二次调用返回false。

2.2 isInterrupted()方法

package com.linyf.demo.thread.stop;

public class StopTest3 {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
        thread.interrupt();
        System.out.println("stop 1 interrupt:" + thread.isInterrupted());
        System.out.println("stop 2 interrupt:" + thread.isInterrupted());
    }
}

执行结果:
在这里插入图片描述
isInterrupted()并未清除状态,所以打印了两个true。

3. 异常法停止线程

有了前面学习过的知识点,就可以在线程中用if语句来判断一下线程是否是停止状态,如果是停止状态,则后面的代码不再运行即可:

package com.linyf.demo.thread.stop;

public class StopTest4 {
    public static void main(String[] args) {
        Thread thread = new MyThread1();
        thread.start();
        try {
            Thread.sleep(2000);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyThread1 extends  Thread{
    @Override
    public void run(){
        for(int i=0; i<500000; i++){
            if(this.interrupted()) {
                System.out.println("线程已经终止, for循环不再执行");
                break;
            }
            System.out.println("i="+(i+1));
        }
    }
}

执行结果:
多线程系列之优雅停止正在运行的线程【十】_第4张图片
如果for语句下面还有语句,还是会继续运行:
多线程系列之优雅停止正在运行的线程【十】_第5张图片
执行结果:
多线程系列之优雅停止正在运行的线程【十】_第6张图片
使用异常解决语句继续运行:

package com.linyf.demo.thread.stop;

public class StopTest4 {
    public static void main(String[] args) {
        Thread thread = new MyThread1();
        thread.start();
        try {
            Thread.sleep(2000);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyThread1 extends  Thread{
    @Override
    public void run(){
        try{
            for(int i=0; i<500000; i++){
                if(this.interrupted()) {
                    System.out.println("线程已经终止, for循环不再执行");
                    throw new InterruptedException();
                }
                System.out.println("i="+(i+1));
            }

            System.out.println("for循环外面的语句,也会执行");
        }catch (InterruptedException e){
            System.out.println("捕获到了线程终端异常。。。");
            e.printStackTrace();
        }
    }
}

执行结果:
多线程系列之优雅停止正在运行的线程【十】_第7张图片

4. 在沉睡中停止

4.1 线程在sleep()状态下停止

package com.linyf.demo.thread.stop;

public class StopTest5 {
    public static void main(String[] args) {
        Thread thread = new MyThread2();
        thread.start();

        thread.interrupt();
    }
}

class MyThread2 extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("线程开始。。。");
            Thread.sleep(200000);
            System.out.println("线程结束。");
        } catch (InterruptedException e) {
            System.out.println("在沉睡中被停止, 进入catch, 调用isInterrupted()方法的结果是:" + this.interrupted());
            e.printStackTrace();
        }
    }
}

执行结果:
多线程系列之优雅停止正在运行的线程【十】_第8张图片
结论:如果在sleep状态下停止线程,会进入catch语句,并且清除停止状态

4.2 线程先停止,再调用sleep()

package com.linyf.demo.thread.stop;

public class StopTest6 {
    public static void main(String[] args) {
        Thread thread = new MyThread3();
        thread.start();
    }
}

class MyThread3 extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("线程开始。。。");
            this.interrupt();
            Thread.sleep(2000);
            System.out.println("线程结束。");
        } catch (InterruptedException e) {
            System.out.println("先停止,再遇到sleep,进入catch异常");
            e.printStackTrace();
        }
    }
}

执行结果:
多线程系列之优雅停止正在运行的线程【十】_第9张图片

5. 暴力停止

使用stop()方法停止线程是非常暴力的:

package com.linyf.demo.thread.stop;

public class StopTest7 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread4();
        thread.start();
        Thread.sleep(2000);
        thread.stop();
    }
}

class MyThread4 extends Thread {
    private static int i = 0;
    @Override
    public void run() {
           while (true) {
               i++;
               System.out.println(i);
           }
    }
}

执行结果:
多线程系列之优雅停止正在运行的线程【十】_第10张图片

6. stop()与java.lang.ThreadDeath异常

调用stop()方法时会抛出java.lang.ThreadDeath异常,但是通常情况下,此异常不需要显示地捕捉。

package com.linyf.demo.thread.stop;

public class StopTest8 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread5();
        thread.start();
    }
}

class MyThread5 extends Thread {
    private static int i = 0;
    @Override
    public void run() {
        try {
            this.stop();
        } catch (ThreadDeath e) {
            System.out.println("调用stop方法, 进入异常catch");
            e.printStackTrace();
        }
    }
}
  1. stop()方法已经作废;
  2. 有可能使一些清理性的工作得不到完成;
  3. 对锁定的对象进行了解锁,导致数据得不到同步的处理,出现数据不一致的问题。

7. 释放锁的不良后果

使用stop()释放锁将会给数据造成不一致性的结果。如果出现这样的情况,程序处理的数据就有可能遭到破坏,最终导致程序执行的流程错误:

package com.linyf.demo.thread.stop;

public class StopTest9 {
    public static void main(String[] args) throws InterruptedException {
        SynchronizedObject synchronizedObject = new SynchronizedObject();
        Thread thread = new MyThread6(synchronizedObject);
        thread.start();
        Thread.sleep(500);
        thread.stop();
        System.out.println(synchronizedObject.getName() + " " + synchronizedObject.getPassword());
    }
}

class MyThread6 extends Thread {
    private SynchronizedObject synchronizedObject;
    public MyThread6(SynchronizedObject synchronizedObject){
        this.synchronizedObject = synchronizedObject;
    }

    public void run(){
        synchronizedObject.printString("b", "bb");
    }
}

class SynchronizedObject {
    private String name = "a";
    private String password = "aa";

    public synchronized void printString(String name, String password){
        try {
            this.name = name;
            Thread.sleep(100000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

执行结果:
在这里插入图片描述

8. 使用return停止线程

将方法interrupt()与return结合使用也能实现停止线程的效果:

package com.linyf.demo.thread.stop;

public class StopTest10 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new MyThread7();
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }
}

class MyThread7 extends Thread {
    @Override
    public void run(){
        int i = 0;
        while (true){
            i++;

            if(this.interrupted()){
                System.out.println("线程被停止了。i="+i);
                return;
            }
            System.out.println("aaaa");
        }
    }
}

执行结果:
多线程系列之优雅停止正在运行的线程【十】_第11张图片

你可能感兴趣的:(java)