最近在基于 SonarQube 对代码进行质量优化,说实话,之前觉得 SonarQube 这种很无聊,但最近静下心来看了一些扫描出来的问题后,发现这种工具作用还是挺大的,能够帮助我们找到代码中的隐藏缺陷,从而夯实基础。本文的议题就是基于 SonarQube Bug 探讨一下线程中断。
在 SonarQube 中的异常为“InterruptedException
should not be ignored“:
即 SonarQube 认为 InterruptedException
异常不应该被忽视,即本文探讨的关键点就是 InterruptedException
异常该如何处理。
理解线程中断
线程中断我觉得有这么几个关键点:
- 中断线程靠的是一个状态标识位
- 标识位的状态传递涉及到线程间的通讯协作
- Java 提供了原生 API 帮助我们传递、判断这个状态,一般也建议平时就使用这些 API 来操作线程中断
InterruptedException
关于 InterruptedException
我觉得有这么几个关键点:
-
如果线程执行了如下几个底层方法,当线程被中断时,会抛出
InterruptedException
java.lang.Object#wait() java.lang.Object#wait(long) java.lang.Object#wait(long, int) java.lang.Thread#sleep(long)
-
抛出
InterruptedException
后会清除中断标识位 -
属于 CheckedException,强制需要捕获处理
处理 InterruptedException
关于处理 InterruptedException
,我觉得关键点在于开发者是否希望上层调用方感知当前线程的中断状态,因为(常规情况下)只有当前线程被中断了才会抛出这个异常,线程中断是一种状态,那么这种状态是否需要传递给上层调用方呢。
很明显 SonarQube 是不赞成忽视 InterruptedException
的。忽视 InterruptedException
会造成一个问题:线程中断的状态丢失,上层调用方将无法感知。
换句话来说,如果你觉得上层调用方没必要感知线程的中断状态,那么你可以忽视它(当然可能显得政治不正确);如果要感知中断状态,要么抛出 InterruptedException
交给上层处理或者 catch
后执行 java.lang.Thread#interrupt
设置中断状态即可。
回到本次扫描的异常代码,其实就是想注册一个钩子做一些后续工作,不存在中断状态的感知问题,所以我个人认为其实不手动设置中断状态也 问题不大。
Runnable shutdownHook = () -> {
try {
//do sth
Thread.sleep(dubboShutdownWait);
//do sth
} catch (InterruptedException e) {
log.error(e);
//Thread.currentThread().interrupt();
}
};
Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook));
而且还有个细节点,这里无非就是想让线程等会,干嘛非得用 java.lang.Thread#sleep(long)
方法呢,还得处理 InterruptedException
,用 java.util.concurrent.locks.LockSupport#parkNanos(long)
不香嘛?
或者是不是也可以这样(就是玩):
private static void workTime(long ms) {
final long l = System.currentTimeMillis();
while (System.currentTimeMillis() <= l + ms) {
}
}
附:完整 SonarQube 描述
为了便于后续查阅,这里完整贴出 SonarQube 的描述:
“InterruptedException” should not be ignored
InterruptedExceptions
should never be ignored in the code, and simply logging the exception counts in this case as “ignoring”. The throwing of theInterruptedException
clears the interrupted state of the Thread, so if the exception is not handled properly the fact that the thread was interrupted will be lost. Instead,InterruptedExceptions
should either be rethrown - immediately or after cleaning up the method’s state - or the thread should be re-interrupted by callingThread.interrupt()
even if this is supposed to be a single-threaded application. Any other course of action risks delaying thread shutdown and loses the information that the thread was interrupted - probably without finishing its task.Similarly, the
ThreadDeath
exception should also be propagated. According to its JavaDoc:If
ThreadDeath
is caught by a method, it is important that it be rethrown so that the thread actually dies.Noncompliant Code Example
public void run () { try { while (true) { // do stuff } }catch (InterruptedException e) { // Noncompliant; logging is not enough LOGGER.log(Level.WARN, "Interrupted!", e); } }
Compliant Solution
public void run () { try { while (true) { // do stuff } }catch (InterruptedException e) { LOGGER.log(Level.WARN, "Interrupted!", e); // Restore interrupted state... Thread.currentThread().interrupt(); } }
See
- MITRE, CWE-391 - Unchecked Error Condition
- Dealing with InterruptedException
References
- https://blog.csdn.net/Dongguabai/article/details/110338023
欢迎关注公众号: