Thread.sleep()挂了吗?

这是我的简单代码,每秒循环(不需要精确),并在必要时开始工作:

while (true) { // check db for new jobs and // kick off thread if necessary try { Thread.sleep(1000); } catch(Throwable t) { LOG.error("", t); } } 

这段代码已经好几个月了。 就在昨天,我们开始遇到问题,其中一个服务器似乎挂在Thread.sleep(1000)方法中 。 IOW – 已经过了一天而且Thread.sleep还没有返回。 我启动了jconsole并获得有关该线程的信息。

 Name: Thread-3 State: TIMED_WAITING Total blocked: 2 Total waited: 2,820 Stack trace: java.lang.Thread.sleep(Native Method) xc.mst.scheduling.Scheduler.run(Scheduler.java:400) java.lang.Thread.run(Thread.java:662) 

Scheduler.java:400是上面的Thread.sleep行。 按照我的预期,jconsole输出不会每秒增加“Total wait”。 事实上,它根本没有变化。 我甚至关闭了jconsole并重新启动它,希望可能会强制刷新,但只会再次获得相同的数字。 我不知道除了jvm错误地挂在sleep命令上之外还有什么其他的解释。 然而,在我这些年里,我对jvm的问题很少,我认为这一定是我的疏忽。

注意:另外需要注意的是没有其他线程处于活动状态。 IOW – cpu几乎空闲。 我读到某个地方,如果另一个线程处于活动状态,Thread.sleep可能会被合法地饿死,但这不是这里的情况。

solaris版本:

 $ uname -a SunOS xcmst 5.10 Generic_141415-08 i86pc i386 i86pc 

java版本:

 $ java -version java version "1.6.0_26" Java(TM) SE Runtime Environment (build 1.6.0_26-b03) Java HotSpot(TM) Server VM (build 20.1-b02, mixed mode) 

您的应用程序是否在虚拟环境中运行 我建议不用VM试试。

除了bdonlan提到的,你可能想要查看ScheduledThreadPoolExecutor 。 我在一个非常类似的项目上工作,这个对象让我的生活变得更轻松,感谢这个小片段。

ScheduleAtFixedRate

如果此任务的执行时间超过其周期,则后续执行可能会延迟,但不会同时执行。

我希望这有帮助!

您是否依赖系统滴答计数来单调增加?

根据我从经验丰富的人那里听到的消息,它( 偶尔 )会发生系统节拍倒退一两个滴答声。 我自己还没有经历过,但如果你依赖于此,这可能会解释发生了什么吗?

编辑:

当我说System.currentTimeMillis() ,我相信我错了。 我认为System.currentTimeMillis()类似于Windows的GetTickCount()函数(即它测量的时间与系统时间无关 ),但事实上,情况似乎并非如此。 所以当然它可以改变,但这不是我的观点:显然,系统计时器测量的滴答计数可以倒退一两滴,甚至忽略系统时间的变化。 不确定这是否有帮助,但感谢Raedwald指出系统时间变化的可能性,因为那不是我的意思。

我知道你查看了jconsole,但是将信号3发送到进程(即kill -3)并在此处发布更多结果线程转储可能很有用。 或者,如果您真的想了解详细信息,那么您可以考虑快速连续地对挂起进程执行一个或多个pstack / jstack转储,以显示线程的确实位置。 有关如何将此信息与java线程转储相关联的信息可在线获取。

此外,通过“我们的一台服务器”,您是说这个问题在一台服务器上是可重现的,但它永远不会在其他服务器上发生? 这表明该服务器存在问题。 检查服务器上的所有内容是否相同,特别是该硬件上没有问题。

最后,这本身可能不是java问题。 Thread.sleep(long)是一种本机方法(直接映射到底层操作系统的线程管理),因此请检查您的操作系统是否是最新的。

您是否考虑过使用Timer & TimerTask 。

这是一个简单的代码片段,可能有所帮助。

 import java.util.Calendar; import java.util.Timer; import java.util.TimerTask; public class Example { public static void main(String args[]) { Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public void run() { Calendar instance = Calendar.getInstance(); System.out.println("time: " + instance.getTime() + " : " + instance.getTimeInMillis()); // check db for new jobs and // kick off thread if necessary } }; int startingDelay = 0; // timer task will be started after startingDelay int period = 1000; // you are using it as sleeping time in your code timer.scheduleAtFixedRate(task, startingDelay, period); } } 

编辑

根据我研究过的讨论, Thread.sleep() is the sign of poorly designed code. 原因是

  • …线程不会失去任何监视器的所有权(来自文档)。
  • 阻止线程执行。
  • 显然它没有给出任何保证,执行将在睡眠时间后开始。
  • 对我来说,使用Thread.sleep()是非常原始的。 有一整套专用于并发的包。

哪一个更好而不是Thread.sleep()? 这提出了另一个问题。 我建议你看看Effective Java一书中的Concurrency章节。

Thread.sleep()在Java编程中不是一个好习惯。 只是Google“Thread.sleep()不好吗?” 你会看到我的观点。

首先,它使程序的其他部分无法访问当前的Thread,特别是如果它是multithreading的。 也许这就是你遇到困难的原因。

其次,如果当前线程是EDT(事件调度线程)并且应用程序具有Swing GUI,那将是灾难性的。

一个更好的选择是Object.wait()

 final Object LOCK = new Object(); final long SLEEP = 1000; public void run() { while (true) { // check db for new jobs and // kick off thread if necessary try { synchronize (LOCK) { LOCK.wait(SLEEP); } } catch (InterruptedException e) { // usually interrupted by other threads eg during program shutdown break; } } } 

也许你可以尝试除Jconsole之外的其他工具来首先确认它在睡眠api中是阻塞的。

例如,手动尝试使用jstack将其多次打印到文件并检查结果。

或者使用更好的工具,例如Youkit(commercail),如果您的组织有权深入分析应用程序,或远程调试(可能无法生产)

或者您可以检查是否在运行期间运行“// check db for new jobs”代码。 通过检查记录,或配置文件,或任何其他方法取决于您的应用程序……..如果检查db非常快,然后睡1秒,如果很可能你总是看到堆栈跟踪睡眠只是因为比较概率….