【问题标题】:Making sure the thread is interrupted确保线程被中断
【发布时间】:2018-07-27 12:38:28
【问题描述】:

我想中断一个线程,该线程在无限循环的每个增量中检查中断标志。退出循环的唯一方法是抛出 InterruptedException。

在可中断线程执行的(不可访问的)代码中的某处,睡眠操作正在声明一个 InterruptedException 和一个捕获异常的 catch 块。在不幸的情况下,线程将在 sleep 方法正在执行时被中断,导致中断标志被重置。在这种情况下,线程不会退出无限的 while 循环。

避免这种情况的最佳策略是什么?下面代码中的主线程在循环中调用中断,直到可中断线程不再活动。这是一个合理的策略吗?有哪些替代方案?

 public class InterruptableThreads extends Thread {

    public static Thread t1 = new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                while (true) {

                    if (Thread.currentThread().isInterrupted()) {
                        throw new InterruptedException();
                    }

                    try {
                        Thread.sleep(5000);
                    } catch (Exception e) {
                        System.out.println("Swallowing exception. Resetting interrupted flag");
                    }

                    System.out.println("t1 run");
                }
            } catch (InterruptedException e) {
                System.err.println("t1 interrupted.");
            }
        }

    });

    public static void main(String[] args) throws InterruptedException {

        t1.start();

        Thread.sleep(4000);

        while (t1.isAlive()) {
            t1.interrupt();
        }

        t1.join(1000);

        System.out.println("Finished!");


    }
}

【问题讨论】:

  • catch block where the exception is swallowed. 这总是一个坏主意。正确的方法是抛出异常(不捕获它)或重置 catch 块中的中断标志。
  • 我知道这是个坏主意。我无权访问这部分遗留代码。
  • 你试过了吗?你确定不可访问的代码有调用可中断方法而不吞下InterruptedException的地方吗?否则你会永远等待,唯一的办法就是调用 Thread.stop()。
  • 我试过了,代码肯定会吞下异常,它的一个不好的副作用是“中断”标志被重置,所以我的条件没有结束while循环围绕此代码。 (这是一个极不可能的星座和罕见的情况,每 5 年发生一次)我的 sn-p 建议一个解决方案,通过在有问题的线程上重复调用中断,希望在某个时刻它会到达它而不是在睡眠中部分。我的问题是这个解决方案是否合法。

标签: java multithreading concurrency


【解决方案1】:

您提出的解决方案包括循环直到线程死亡应该可以工作。我不知道这是否可行,但您可能希望通过在该循环中包含 join 语句来避免主动旋转:

    while (t1.isAlive()) {
        t1.interrupt();
        try {
            t1.join(1000);

        } catch(InterruptedException e) {
            ...
        }
    }

另一种方法是将中断吞咽代码的执行与可中断线程的主循环隔离开来:

public static Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<?> f = null;

        try {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    throw new InterruptedException();
                }

                f = executor.submit(new Runnable() {
                    // swallowing code
                });

                try {
                   f.get();

                } catch(CancellationException e) {
                  ...
                } catch(ExecutionException e) {
                  ...
                } // interrupted exceptions will be thrown from get() but not caught here                 

                System.out.println("t1 run");
            }
        } catch (InterruptedException e) {
            System.err.println("t1 interrupted.");
            if (f != null) {
              f.cancel(true);
            }

        } finally {
            executor.shutdownNow();
        }
    }

});

f.get() 将使您能够捕获t1 的中断,而不管执行程序服务在执行外部代码时启动的线程的状态如何,并且可能“休眠”。

但是添加另一个线程会带来额外的复杂性和与之相关的风险。

【讨论】:

  • 其实我的代码中有一个join语句。问题是,它在另一个线程完成之前超时,或者如果没有超时,它将永远冻结。我将分析提议的替代方案的影响。谢谢。
【解决方案2】:

Brian Goetz 在Java Concurrency in Practice 中概述了最佳策略。从内存中,您从 catch 块中重新中断线程,或者抛出异常。

另外,尽可能捕捉最窄的异常,而不是Exception

                if (Thread.currentThread().isInterrupted()) {
                    throw new InterruptedException();
                }

                try {
                    Thread.sleep(5000);
                } catch (InterrutedException e) {         // <---- Narrow the scope here
                    System.out.println("Swallowing exception. Resetting interrupted flag");
                    Thread.currentThread().interrupt();   // <----- NEW
                }

【讨论】:

  • 据我理解的问题,OP理解代码应该做什么,但不能改变它。
  • 在这种情况下,OP 被搞砸了。我建议暗中反编译并重新编译字节码以将其潜入其中。说真的,也许他应该只设置一次中断标志,然后当线程没有退出时指出实际错误在线程代码中,而不是他的代码。
  • 我无权访问异常捕获。问题是我在主线程中的中断方法是否是一种有效的解决方法。
猜你喜欢
  • 2014-10-08
  • 2021-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-08
  • 2015-11-02
  • 2013-04-28
相关资源
最近更新 更多