欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Java 多线程:如何停止/中断正在运行的线程?

最编程 2024-03-05 10:03:24
...

# 面试题:


  • 如何正确地停止/中断一个线程
  • 哪些情况下线程会停止
  • 如何处理不可中断的阻塞


# 核心思想


  • 使用interrupt()来通知,而不是强制。


# 代码演示


  • 场景1:run()方法中没有sleep()/wait()等会响应中断的方法。
    1.1  线程未处理中断:

/**
 * 正确停止线程---run()方法内没有sleep()或者wait()方法-未处理中断信号
 *
 * @author futao
 * @date 2020/6/6
 */
public class StopThreadWithoutSleepWait implements Runnable {
    @Override
    public void run() {
        unHandleInterrupt();
    }
    /**
     * 未处理中断
     */
    public void unHandleInterrupt() {
        int num = 0;
        //打印最大整数一半的范围内10000的倍数
        while (num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是10000倍数");
            }
            ++num;
        }
        System.out.println("任务执行完毕");
    }
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThreadWithoutSleepWait());
        //启动线程
        thread.start();
        //增加子线程处于运行状态的可能性
        Thread.sleep(500L);
        //尝试中断子线程
        thread.interrupt();
    }
}


  • 期望:子线程在执行500毫秒之后停下来。
  • 结果:线程并没有停下来。原因是:我们并未处理线程的中断信号。


网络异常,图片无法展示
|


网络异常,图片无法展示
|

  • 1.2 对程序进行改进:响应中断。


  • 在while循环条件中判断当前线程是否被中断(Thread.currentThread().isInterrupted()),如果未被中断才继续执行,被中断则跳出while循环。

package com.futao.learn.threads.c_如何停止线程;
/**
 * 正确停止线程---run()方法内没有sleep()或者wait()方法
 *
 * @author futao
 * @date 2020/6/6
 */
public class StopThreadWithoutSleepWait implements Runnable {
    @Override
    public void run() {
        handleInterrupt();
    }
    /**
     * 响应中断
     */
    public void handleInterrupt() {
        int num = 0;
        //加入线程未被中断的条件
        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) throws InterruptedException {
        Thread thread = new Thread(new StopThreadWithoutSleepWait());
        //启动线程
        thread.start();
        //增加子线程处于运行状态的可能性
        Thread.sleep(500L);
        //尝试中断子线程
        thread.interrupt();
    }
}


  • 期望:线程在500毫秒之后响应中断,停下来。
  • 结果:线程成功响应中断,提前结束。


网络异常,图片无法展示
|

网络异常,图片无法展示
|

  • 总结可得出:线程调用者可以向线程发出中断请求,但是线程中断的权利控制在线程代码的编写者是否响应了你的中断请求。线程代码的编写者比调用者更加了解线程应不应该被停止,何时停止。


  • 场景2:run()方法中存在sleep()/wait()等会响应中断的方法。(响应中断的方法会抛出InterruptedException)
    2.1 sleep()在while循环外

package com.futao.learn.threads.c_如何停止线程;
/**
 * 中断线程-run()方法中有sleep()或者wait()方法
 *
 * @author futao
 * @date 2020/6/6
 */
public class StopThreadWithSleep {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            int num = 0;
            while (!Thread.currentThread().isInterrupted() && num <= 300) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的整数倍");
                }
                ++num;
            }
            try {
                //sleep()方法会响应中断,且响应中断的方式为抛出InterruptException异常--- sleep interrupted
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程执行完毕");
        });
        //启动线程
        thread.start();
        //等待while循环执行完毕
        Thread.sleep(200L);
        //当线程处于sleep()状态时进行中断
        thread.interrupt();
    }
}


  • 预期:程序执行完while循环之后,阻塞在sleep()方法,此时进行中断,sleep()方法响应该中断,抛出InterruptedException,打印异常堆栈。
  • 测试:符合预期。


网络异常,图片无法展示
|


  • 2.2 无法停止的线程:sleep()方法在while循环内。
  • 你预期下面代码的执行结果是怎样的?

/**
 * 3. 无法停止的线程
 *
 * @author futao
 * @date 2020/6/6
 */
public class CantStopThread {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            int num = 1;
            while (num <= 1000 && !Thread.currentThread().isInterrupted()) {
                if (num % 2 == 0) {
                    System.out.println(num + "是2的整数倍");
                }
                ++num;
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行完毕");
        });
        //启动线程
        thread.start();
        //主线程休眠500毫秒
        Thread.sleep(500L);
        //中断线程
        thread.interrupt();
    }
}


  • 预期:线程在第一次进入while循环时,进入休眠1000毫秒状态,在500毫秒时主线程向子线程发出中断信号,sleep()方法响应中断,打印异常堆栈,下次再进入while循环时,因为线程被设置成了中断状态,所以while中条件不成立,不应该继续执行。但是实际上是这样吗?
  • 结果:slee()响应了中断,打印了异常堆栈。但是线程并没有停下来,而是继续执行。就像什么都没有发生一样。


image.png


  • 原因:sleep()在响应了中断之后,清除了线程的中断状态。那么while判断时不知道线程被中断了。


  • 查看sleep()方法的描述:当InterruptedException异常被抛出后,线程的中断状态将被清除。


image.png

类似的,查看Object.wait()的方法描述。

image.png

  • 类似的会响应中断的方法还有那些?
  • 响应中断的方法总结
  • Object.wait()/wait(long)/wait(long,int)
  • Thread.sleep(long)/sleep(long,int)
  • Thread.join()/join(long)/join(long,int)
  • juc.BlockingQueue.take()/put(E)
  • juc.Lock.lockInterruptibly()
  • juc.CountDownLatch.await()
  • juc.CyclicBarrier.await()
  • juc.Exchanger.exchange(V)
  • jio.InterruptibleChannel相关方法
  • jio.Selector相关方法
  • 那么,线程响应中断后应该怎么处理。


# 线程中断的最佳实践:


  • 传递中断
  • 不想或无法传递:恢复中断
  • 核心思想:不应屏蔽中断
  1. 传递中断:在方法签名中将中断异常抛出,而不是生吞,交给调用者处理。

/**
 * 正确停止线程的方式1-抛出中断
 * 优先在方法签名中抛出该异常
 *
 * @author futao
 * @date 2020/6/6
 */
public class RightWayToStopThread implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("running...");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                e.printStackTrace();

						

上一篇: JAVA 中的跳转语句 - break、continue

下一篇: 安全启动