Thread.join带有“重试”模式

时间:2014-12-08 11:57:18

标签: java android multithreading join

使用Thread查看示例代码,我经常会遇到此重试模式。

boolean retry = true;
while (retry) {
    try {
        myThread.join();
        retry = false;
    } catch (InterruptedException e) {
        // Nothing ...
    }
}

join()应该永远等待。

如果当前线程在join之前或期间被中断并因此收到InterruptedExceptionmyThread实际加入了吗?

这是一些切割残留物变成了一种模式吗?

2 个答案:

答案 0 :(得分:2)

如果Thread,则join() Thread InterruptedException join() Threadinterrupt()应该永远等待或直到调用InterruptedExceptionInterruptedException - ed

首先,您需要考虑InterruptedException是否真的可以发生。如何处理它有几种可能性。你可以:

  • 吞下它。 警告只有在您确定永远不会发生Thread时才会这样做。 (你确定吗?真的吗?)
  • 再次设置中断标志。如果您的代码不知道如何处理它,那么这就是您所做的,但是Thread正在做其他可能对run()感兴趣的东西。
  • 传播它。如果您当前的方法不知道如何处理它,这可能是最好的解决方案,但调用者可能知道。
  • 处理它。也许您当前的InterruptedException只是想在收到InterruptedException后停止(通过完成InterruptedException方法)。
  • 将其包裹在另一个例外中。这有两种变体。
    • 如果log()首先不应该发生并且显然是错误,您可能希望将其包装在断言中。
    • 如果您的方法绑定到不允许InterruptedException的特定合同,您可能希望将其包装在另一个例外中。

无论如何,我强烈建议以某种方式 InterruptedException public static void joinThread(final Thread thread) { while (true) try { thread.join(); return; } catch (final InterruptedException e) { Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e); } } 。如果您没有记录它,并且它发生了,如果没有关于public static void joinThread(final Thread thread) { try { thread.join(); } catch (final InterruptedException e) { Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e); } } 的日志信息,程序的行为可能很难理解。

吞下它

警告如果你绝对确定在你的情况下这是可以的,那么只能吞下它。 你被警告了。

interrupted

作为其变体,您可以在不重试的情况下忽略它。

Thread

再次设置InterruptedException标志。

如果public static void joinThread(final Thread thread) { try { thread.join(); } catch (final InterruptedException e) { Logger.getGlobal().log(Level.INFO, "Unexpected InterruptedException, resetting flag", e); Thread.currentThread().interrupt(); } } 正在执行其他操作,您就会执行此操作,但您的代码不知道如何处理Thread,但其他代码也知道。

interrupted

但要注意!如果其他代码真的知道该怎么做,这只是个好主意。如果其他代码执行相同的操作,并且东西是循环的(这通常是InterruptedException s的情况),那么您只需将好的阻塞代码转换为忙碌等待的垃圾,因为{{1 flag永远不会被正确清除。虽然这个解决方案在许多博客文章中被宣传为最佳或真正的解决方案,但只要没有任何代码知道如何处理public static void joinThread(final Thread thread) throws InterruptedException { try { thread.join(); } catch (final InterruptedException e) { Logger.getGlobal().log(Level.FINE, "Got InterruptedException, propagating it to the caller", e); throw e; } } 并且实际上保持标志清除而不是重置它,这是无稽之谈。 / p>

传播

如果方法本身不知道如何处理它,这是最好的解决方案,但调用者可能知道。

public static void joinThread(final Thread thread) {
    try {
        thread.join();
    } catch (final InterruptedException e) {
        Logger.getGlobal().log(Level.FINE, "Got InterruptedException, handling it", e);
        // ...whatever...
    }
}

如果来电者做了很好的事情,你可以删除不必要的部分,如记录和重新投掷。

处理它。

如果方法本身知道在被中断时,它必须做某事,它可以简单地捕获异常并做任何预期的事情。

Thread.interrupt()

包装它。

如果不应该发生异常,因为应用程序不支持中断,并且首先不应该调用ThreadGroup.interrupt()AssertionError,将异常转换为{{ 1}}是要走的路。 有两种可能性。

// Only throws AssertionError if assertions are enabled.
public static void joinThread(final Thread thread) {
    try {
        thread.join();
    } catch (final InterruptedException e) {
        Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
        assert false : e;
    }
}

// Always throws AssertionError.
public static void joinThread(final Thread thread) {
    try {
        thread.join();
    } catch (final InterruptedException e) {
        Logger.getGlobal().log(Level.FATAL, "Unexpected InterruptedException", e);
        throw new AssertionError(e, "Unexpected Thread interruption.");
    }
}

如果要传播InterruptedException但调用者不支持该异常且无法更改,则可以将其包装到支持的异常中。这是否有效并且是一个好主意在很大程度上取决于呼叫者。

public static void joinThread(final Thread thread) {
    try {
        thread.join();
    } catch (final InterruptedException e) {
        Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
        throw new SomeException(e, "Unexpected Thread interruption.");
    }
}

最后的笔记

有一些讨论认为吞咽总是很糟糕。这不是真的。如果你有100行代码,并且出于某种原因在某处做Thread.sleep(),吞咽通常很好。一如既往,这取决于。最后,Thread.interrupt()的语义与SIGHUP没有太大区别,SIGHUP通常会被忽略。

还有一些讨论是否将其变成AssertionError是一个好主意。 AssertionError是一个未经检查的Throwable,甚至不是Exception,这意味着编译器不会强制代码处理它,并且大多数代码都不会编写来处理它 - 这是故意的,因为使用AssertionError我们说这里是一个非常意外的错误,代码不知道如何处理它因此想要停止。 AssertionError是否终止VM取决于由于AssertionError被提升到受影响的UncaughtExceptionHandler的{​​{1}}而将被删除的线程。< / p>

答案 1 :(得分:1)

如果myThread.join()调用中断,则线程未加入。 myThread将继续运行,不知道调用join的线程已被中断。

我自己并没有看到这种模式,我说这是一种可怕的模式。你永远不应该忽略InterruptedException这种方式。有关如何正确处理InterruptedExceptions的说明,请参阅this post