一 场景
Intelli IDEA会对在循环中使用线程休眠的代码报出警告
。涉及该问题的代码十分简单,可抽象为以下过程。
/**
* 标记量
*/
public static boolean FLAG = false;
/**
* 主方法
*
* @param args 参数集
* @throws InterruptedException 中断异常对象
*/
public static void main(String[] args) throws InterruptedException {
while (true) {
Thread.sleep(3000);
if (FLAG) {
doSomething();
break;
}
}
}
/**
* 做一些事情
*/
public static void doSomething() {
System.out.println("Hello World!!!");
}
简单的解释一下,就是我想在标记量为true时做一些事情,但是我无法确定标记量被设置为true的时间,因此通过循环来进行无限检查,但我又不想检查的太过频繁,因此每次检查间隔三秒。这看起来是一段非常自洽的代码,但是Intelli IDEA却对之报出了警告。
Call to ‘Thread.sleep()’ in a loop, probably busy-waiting
在循环中调用Thread.sleep(),可能正在忙等待
这是警告而非异常,代表代码依旧可以被正常编译及运行,但是并不推荐这样做。
二 原因
可能导致的严重性能问题是不推荐在循环中使用线程休眠的根本原因
。Java线程实现采用内核线程实现,线程的休眠及唤醒(状态切换)需借助操作系统进行,这是一个极其耗时耗力的操作。在线程休眠或运行时间较长的情景下,其对性能的影响还不算明显,因为对线程状态的切换并不频繁。但若线程休眠及运行的时间都很短(例如毫秒/秒,文中案例就是一个典型案例),系统将频繁的对线程状态进行切换,导致严重的性能损耗,并对着循环次数的递增而放大。
三 解决方案
使用调度程序替换在循环中使用线程休眠的操作
。如果对文中示例有所了解,会发现其本质是一个定时问题,每间隔一段时间进行操作直至达到条件,按文中示例,便是每隔3秒检查标记量并做一些事情,因此我们完全可使用调度程序进行替换。Java中的调度API不少,可供选择的方案很多,这里着重推荐
Timer
及
ScheduledExecutorService
方案,其各自适用于单线程及多线程的环境中。