这是我的简单代码,每秒循环(不需要精确),并在必要时开始工作:
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)
答案 0 :(得分:7)
除了bdonlan提到的内容之外,您可能还需要了解ScheduledThreadPoolExecutor。我在一个非常相似的项目类型上工作,这个对象让我的生活变得更轻松,感谢这个小片段。
如果此任务的执行时间超过其周期,那么 后续处决可能会延迟,但不会同时执行 执行。
我希望这有帮助!
答案 1 :(得分:5)
你依赖对系统滴答计数进行单调增加吗?
根据我从有经验的人那里听到的,它(偶尔)会发生系统滴答,倒退一两个滴答。我自己还没有经历过,但如果你依赖于此,这可能会解释发生了什么吗?
当我说System.currentTimeMillis()
时,我相信我错了。我认为System.currentTimeMillis()
类似于Windows'GetTickCount()
函数(即它测量系统时间独立的时间),但事实上,这似乎不是是这样的。那么当然它可以改变,但那是不我的观点:显然,系统计时器测量的滴答计数可以也倒退一个滴答或两次,甚至忽略系统时间的变化。不确定这是否有帮助,但感谢Raedwald指出系统时间变化的可能性,因为那不是我的意思。
答案 2 :(得分:3)
我知道您查看了jconsole,但将信号3发送到进程(即kill -3)并在此处发布更多结果线程转储可能很有用。或者,如果您真的想了解详细信息,那么您可以考虑快速连续地对挂起进程执行一个或多个pstack / jstack转储,以显示线程的确实位置。有关如何将此信息与java线程转储相关联的信息可在线获取。
此外,通过“我们的一台服务器”,您是说这个问题在一台服务器上是可重现的,但它永远不会在其他服务器上发生?这表明该服务器存在问题。检查服务器上的所有内容是否相同,特别是该硬件上没有任何问题。
最后,这本身可能不是java问题。 Thread.sleep(long)是一种本机方法(直接映射到底层操作系统的线程管理),因此请检查您的操作系统是否是最新的。
答案 3 :(得分:2)
以下是可能有用的简单代码段。
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()?这提出了另一个问题。我建议您查看本书Effective Java
中并发一章。
答案 4 :(得分:1)
Thread.sleep()在Java编程中不是一个好习惯。只是Google“Thread.sleep()是不是很糟糕?”你会看到我的观点。
首先,它使程序的其他部分无法访问当前的Thread,特别是如果它是多线程的。也许这就是你遇到困难的原因。
其次,如果当前线程是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 e.g. during program shutdown
break;
}
}
}
答案 5 :(得分:0)
也许你可以尝试除Jconsole之外的其他工具来首先确认它是睡眠api中的阻塞。
例如,手动尝试使用jstack将其多次打印到文件并检查结果。
或者使用更好的工具,例如Youkit(commercail),如果您的组织有权深入分析应用程序,或远程调试(可能无法生产)
或者您可以检查是否在运行期间运行“// check db for new jobs”代码。通过检查记录,或配置文件,或任何其他方法取决于您的应用程序........如果检查db非常快,然后睡1秒,如果你很可能总是看到堆栈跟踪睡眠只是因为比较概率......