如何处理:循环调用'Thread.sleep()',可能是忙等待
Posted
技术标签:
【中文标题】如何处理:循环调用\'Thread.sleep()\',可能是忙等待【英文标题】:How to deal with: Call to 'Thread.sleep()' in a loop, probably busy-waiting如何处理:循环调用'Thread.sleep()',可能是忙等待 【发布时间】:2021-06-20 04:30:34 【问题描述】:各位大佬如何处理这样的代码和警告?
private void listenOnLogForResult()
String logs = "";
int timeCounter = 1;
while (logs.isEmpty())
try
timeCounter++;
Thread.sleep(2000); // Wait 2 seconds
catch (InterruptedException e)
log.error(e.getLocalizedMessage(), e);
if (timeCounter < 30)
logs = checkLogs()
else
logs = "Time out";
我需要暂停当前线程 2 秒以等待文件被填充,但我的 Intelij Rise 出现问题。 而且我从声纳收到错误: SonarLint:要么重新中断此方法,要么重新抛出“InterruptedException”。
我已经尝试过很多ExecutorService
,但它总是在单独的线程中运行,我需要暂停当前线程。
请帮忙..
【问题讨论】:
这可能会对***.com/questions/54394042/… 有所帮助。对于您的例外,您只是记录它并且while循环继续。我猜这是无限循环的潜在候选者 【参考方案1】:忙等待警告
这是来自 intellij 的可疑警告,从某种意义上说,您所做的通常是直接需要的。换句话说,它正在检测一种过度使用的模式,但其使用量不能减少到 0。因此,可能正确的解决方案是告诉 intellij 在这里闭嘴。
它正在查看的问题是不是Thread.sleep
。那不是问题。但是,intellij 的这种模式检测器需要它来找到这种情况,但这并不是它所抱怨的,这可能有点难以理解。
IntelliJ 担心的是,您会无缘无故地不断地重新检查log.isEmpty()
。 这段代码的while
有问题,而不是睡眠。它希望看到您调用某种logs.poll()
方法的代码,该方法将等到它被新日志出现的行为主动唤醒。
如果这一切都在单个 java 进程中运行,那么您确实可以重写整个系统(包括重写此处的 log
,以及对 checkLogs()
方法的完全重新想象:而不是去出来检查一下,无论是制作日志,都需要唤醒这段代码。
如果不是,您可能需要告诉 intellij 关闭它:如果不重新设计完整的系统,您正在做的事情是不可避免的。
重新中断警告
这里有一些可悲的异常处理。
一般的异常处理
不要编写 catch
块来记录某些内容并继续移动。这是非常糟糕的错误处理:系统的变量和字段现在处于未知状态(您刚刚捕获并记录了一些内容:当然这意味着您不知道发生了什么条件导致这行执行发生!),并且然而代码将继续前进。 “捕获异常并继续运行”风格的代码极有可能会导致更多异常:通常,在未知状态下运行的代码迟早会崩溃和烧毁。
然后,如果以相同的方式处理崩溃和烧毁(抓住它,记录它,继续前进),那么你会得到另一个崩溃和烧伤。你最终得到的代码会在遇到问题时打印186个异常到日志,除了第一个之外,它们都完全不相关。那个坏yuyu。
您还使调用代码完全无法恢复。异常的要点是它们需要无休止地向上冒泡:要么异常被实际上知道如何处理问题的代码捕获(并且记录它不是处理用它!),这是你不可能做到的,或者,代码异常应该一直冒泡到入口点处理程序,这是记录错误并中止入口点处理程序的正确位置。
入口点处理程序是通用模块或应用程序运行器;开箱即用,最终调用您的 psv main()
方法的 java.exe
本身的代码是最明显的“入口点运行器”,但还有更多:Web 框架最终会调用您应该处理的一些代码一个网络请求:你的代码类似于psv main()
:它是入口点,调用它的网络框架中的代码是入口点运行器。
入口点运行者有充分的理由catch (Throwable t)
,并主要花费他们的 catch 块记录它,尽管他们通常应该记录的不仅仅是异常(例如,Web 处理程序应该记录请求详细信息,例如发送了哪些 HTTP 参数以及它是哪个路径请求,可能是标头等)。 不过,任何其他代码都不应这样做。
如果您不知道该做什么并且不想考虑该异常可能意味着什么,那么正确的“无论如何,只需编译 javac”代码策略是将异常类型添加到您的 throws
行。如果这不可行,catch 块中的正确代码是:
catch (ExceptionIDoNotWantToThinkAboutRightNow e)
throw new RuntimeException("Uncaught", e);
这将确保代码不仅会愉快地继续前进,在未知状态下运行,还将确保您在日志中获得完整的详细信息,并确保调用代码可以捕获并处理它,并确保任何自定义诸如 HTTP 请求详细信息之类的日志信息有机会记录到日志中。双赢双赢。
特别是这个案例:InterruptedEx 是什么意思?
当在该 java 进程中运行的某些代码调用 yourThread.interrupt()
时,InterruptedException
可能会发生,并且不可能以任何其他方式发生。如果用户按下 CTRL+C,或者进入任务管理器并点击“结束进程”,或者如果你的安卓手机决定是时候让你的应用退出,因为其他东西需要内存 - 无 这些情况可能会导致 InterruptedExceptions。你的线程只是在中途被java杀死(如果你想对关闭采取行动,请使用Runtime.getRuntime().addShutdownHook
)。唯一的方法是让一些代码调用.interrupt()
,而核心库中的任何内容都不会这样做。因此,InterruptedException 意味着您认为“在此线程上调用 .interrupt()”意味着什么。这取决于你。
最常见的定义实际上是“我请你停下来”:只是很好地关闭线程。一般来说,如果你想退出整个虚拟机,尝试很好地关闭线程是不好的(只需调用System.shutdown
- 你已经需要处理用户按 CTRL+C,为什么要以不同的方式编写两次关闭代码?) - 但是有时你只想让一个线程停止。因此,通常放在catch (InterruptedException e)
块中的最佳代码是just return;
,仅此而已。不要记录任何东西:“中断”是故意的:你写的。这很可能不在您的代码库中,并且 InterruptedException 没有实际意义:它永远不会发生。
在您的特定代码中,如果您的代码决定停止记录器线程,记录器线程会将某些内容记录到错误日志中,然后会缩短其 2 秒的等待时间以立即检查日志,然后只需继续前进。这听起来完全没用。
但是,它意味着任何你想要的。如果你想要一个能力,例如用户点击“立即强制检查日志”按钮,然后您可以定义中断日志记录线程只是缩短 2 秒(但随后只有一个带有注释的空 catch 块,说明这是您设计的方式,显然不要记录它)。如果您还想要一个按钮来“停止日志记录线程”,请使用 AtomicBoolean 来跟踪“正在运行”状态:当“停止日志刷新”按钮被点击时,将 AB 设置为“假”,然后中断线程:然后你粘贴的代码需要检查AB和return;
如果是false
就关闭线程。
【讨论】:
我觉得奇怪的是,您在这里热情地谴责异常处理方法(使用“可悲”之类的词和大量粗体文本),然后在最后一段中承认“您可以定义中断日志记录线程只是缩短了 2 秒”。我认为这正是 OP 试图将其定义为的,而且我也经常使用这种模式。事实上,除非你正在构建一个带有中断机制的复杂多线程应用程序,否则这通常是中断的意思,即它的意思是“嘿 SomeThread,停止睡眠,继续你的工作” @AdamBurley 这显然是一个错误的命题。如果 OP 打算这样做,他们显然不会在ERROR
级别发送日志消息。你的次要想法是中断通常意味着“停止等待并立即检查,但继续做你的事情”也只是你编造的东西。没关系,但这不是“正确的选择”,甚至不是“最常见的选择”。这是一个选择。和其他许多人一样好。以上是关于如何处理:循环调用'Thread.sleep()',可能是忙等待的主要内容,如果未能解决你的问题,请参考以下文章