23

我最近继承了一个几乎没有线程安全的大型 Java 应用程序。我目前正在做的是让所有线程正确处理被中断而不是使用非常糟糕的Thread.stop().

部分问题是我不知道清除中断标志的每个方法调用。

目前我知道以下将清除中断标志:

Thread.interrupted()
Thread.sleep(long)
Thread.join()
Thread.join(long)
Object.wait()
Object.wait(long)

我还缺少什么?谢谢

4

3 回答 3

49

部分问题是我不知道清除中断标志的每个方法调用。

重要的是要澄清以下方法只需调用它们即可清除中断标志:

Thread.interrupted()
Thread.isInterrupted(true) -- added to your list

出于这个原因Thread.currentThread().isInterrupted(),应该始终使用它。

以下方法将通过立即抛出来清除中断标志,InterruptedException如果它们被调用然后线程被中断,或者如果线程已经被中断然后它们被调用(参见下面的junit代码)。所以不是清除标志的方法,而是抛出异常。

您的初始清单:

Thread.interrupted()
Thread.sleep(long)
Thread.join()
Thread.join(long)
Object.wait()
Object.wait(long)

添加到您的列表中:

Thread.sleep(long, int)
Thread.join(int, long)
Thread.isInterrupted(true)
Object.wait(int, long)
BlockingQueue.put(...)
BlockingQueue.offer(...)
BlockingQueue.take(...)
BlockingQueue.poll(...)
Future.get(...)
Process.waitFor()
ExecutorService.invokeAll(...)
ExecutorService.invokeAny(...)
ExecutorService.awaitTermination(...)
CompletionService.poll(...)
CompletionService.take(...)
CountDownLatch.await(...)
CyclicBarrier.await(...)
Semaphore.acquire(...)
Semaphore.tryAcquire(...)
Lock.lockInteruptibly()
Lock.tryLock(...)

请注意,捕获任何代码的正确模式InterruptedException是立即重新中断线程。我们这样做以防其他人依赖该thread.isInterrupted()方法:

try {
    ...
} catch (InterruptedException e) {
    // immediately re-interrupt the thread
    Thread.currentThread().interrupt();
    // log the exception or [likely] quit the thread
}

JUnit 代码演示了其中的一些:

assertFalse(Thread.currentThread().isInterrupted());
// you can do this from another thread by saying: someThread.interrupt();
Thread.currentThread().interrupt();
// this method does _not_ clear the interrupt flag
assertTrue(Thread.currentThread().isInterrupted());
// but this one _does_ and should probably not be used
assertTrue(Thread.interrupted());
assertFalse(Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();
assertTrue(Thread.currentThread().isInterrupted());
try {
    // this throws immediately because the thread is _already_ interrupted
    Thread.sleep(1);
    fail("will never get here");
} catch (InterruptedException e) {
    // and when the InterruptedException is throw, it clears the interrupt
    assertFalse(Thread.currentThread().isInterrupted());
    // we should re-interrupt the thread so other code can use interrupt status
    Thread.currentThread().interrupt();
}
assertTrue(Thread.currentThread().isInterrupted());
于 2012-09-09T13:30:02.457 回答
16

通用约定如下:任何抛出InterruptedException(+ Thread.interrupted()) 的方法都会清除中断标志。

因此,为了使您的线程可中断,您需要找到所有InterruptedException被捕获的地方,而无需追溯或恢复中断标志。由于InterruptedException是已检查异常,因此不难做到。

于 2012-05-01T18:04:09.167 回答
3

这是一个超级有趣的例子:

1.1.4 版本之前的 ch.qos.logback.core.AsyncAppenderBase 捕获并吞下 InterruptedException 而不重置线程上的标志。

所以,如果你使用任何路由到这个记录器的东西(比如 slf4j),它会默默地吃掉你的线程中断状态。'因为,我的意思是,谁不会在每个可能的日志操作之前和之后检查线程中断状态?

于 2016-05-13T10:06:45.920 回答