一、原理

使用interrupt来通知线程停止,而不是强制停止

Java中最多也就是告诉线程该停止了,而线程本身有最高决定权,是否停止,何时响应停止


 二、正确的停止方法(interrupt)

 

1、普通情况下线程停止

  • 使用其他线程通知想要停止的线程停止
  • 在应该停止的线程里要有响应停止代码,否则也不会停止,使用Thread.currentThread().isInterrupted()来检查是否停止
/**
 * run方法内没有sleep或wait方法时,停止线程
 *
 * @author wjh
 * @date 2020-03-04 14:01
 */
public class RightWayStopThreadWithoutSleep implements Runnable {
    @Override
    public void run() {
        int num = 0;
        // Thread.currentThread().isInterrupted()检查当前线程是否中断
        while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是10000的倍数");
            }
            num++;
        }
        System.out.println("任务运行结束");
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 通知线程停止
        thread.interrupt();
    }
}

运行结果:

1、不通知停止

implements Runnable 停止线程 停止线程的方法_执行过程

2、通知停止,比正常结束提前了很多

implements Runnable 停止线程 停止线程的方法_ide_02

2、在线程阻塞的情况下停止线程

如果线程处于阻塞状态,该线程响应停止的方法是抛出一个异常

/**
 * 带有sleep的中断线程的写法
 *
 * @author wjh
 * @date 2020-03-04 14:24
 */
public class RightWayStopThreadWithSleep {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (!Thread.currentThread().isInterrupted() && num <= 300) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num++;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(500);
        thread.interrupt();
    }
}

结果: 

implements Runnable 停止线程 停止线程的方法_System_03

3、如果线程在每次迭代后都阻塞

如果执行过程中,每次循环都会调用sleep或者wait等待,则不需要每一次检查都检查是否以中断

/**
 * 如果执行过程中,每次循环都会调用sleep或者wait等待,则不需要每一次检查都检查是否以中断
 *
 * @author wjh
 * @date 2020-03-04 14:36
 */
public class RightWayStopThreadWithSleepEveryLoop {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            try {
                while (num <= 10000) {
                    if (num % 100 == 0) {
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                    Thread.sleep(10);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

结果:

implements Runnable 停止线程 停止线程的方法_System_04

如果在while内使用try/catch,会使中断无效 

public class CantInterrupt {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 10000) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍数");
                }
                num++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

结果:

implements Runnable 停止线程 停止线程的方法_System_05


 三、最佳实践

1、优先选择:传递中断

/**
 * 最佳实践:catch了InterruptedException之后的优先选择
 * 在方法签名中抛出异常
 * 这样在run方法中就会强制try/catch
 *
 * @author wjh
 * @date 2020-03-04 14:53
 */
public class RightWayStopThreadInProd implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("go");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                // 保存日志,停止程序
                System.out.println("保存日志");
                e.printStackTrace();
            }
        }
    }

    // 将异常加到方法签名
    private void throwInMethod() throws InterruptedException {
        Thread.sleep(2000);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

2、不想或无法传递:回复中断

/**
 * 最佳实践2:在catch中调用Thread.currentThread().interrupt()来恢复设置中断状态,以便于在后续的执行过程中,依然能够检查到刚才发生的中断
 *
 * @author wjh
 * @date 2020-03-04 14:53
 */
public class RightWayStopThreadInProd2 implements Runnable {
    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Interrupted程序运行结束");
                break;
            }
            System.out.println("go");
            reInMethod();
        }
    }

    private void reInMethod() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

结果

implements Runnable 停止线程 停止线程的方法_执行过程_06


 四、能够响应中断的方法



五、错误的停止方法 

1、被弃用的stop、suspend和resume方法

2、用volatile设置boolean标记位