我有一些代码可以安排执行一次性任务,并反复执行此操作。它看起来像这样。
public static void main(String[] args)
{
while(true)
{
....
TimerTask closeTask = new CloseTask(cli);
Timer timer = new Timer(true);
timer.schedule(closeTask, (long) (iPeriod * 60 * 1000));
...
}
}
public class CloseTask extends TimerTask
{
Client client;
CloseTask(Client in_client)
{
client = in_client;
}
public void run()
{
try
{
for(int iRetries = 0; state == OPEN; iRetries++)
{
logger.log_trade_line_grablock( "Thread " + Thread.currentThread().getId() + ": About to send message", true, true, true, true, true);
client.send_mesg("close");
logger.log_trade_line_grablock( "Waiting 5 seconds before retrying ", true, true, true, true, true);
Thread.sleep(5000);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
run()
类中CloseTask
方法的意图是循环,直到state
变量从OPEN
状态变为其他状态。但是,间歇性地,计时器线程只会消失,而state
仍然等于OPEN
,我知道通过每5分钟打印出当前运行的线程的所有线程ID。
所以我的问题:
1)我能想到的唯一解释是CloseTask
对象抛出未捕获的异常。那是对的吗?
2)如果1)是正确的,为什么我的try catch块没有捕获这些异常?
3)如果1)是正确的,有没有办法捕捉这些未经过未捕获的例外?
感谢您对此问题的任何见解。
答案 0 :(得分:2)
您正在创建一个Timer
实例,但不确保它不会被垃圾收集。
在对Timer对象的最后一次实时引用消失并且所有未完成的任务都已完成执行之后,计时器的任务执行线程正常终止(并且变为垃圾回收)。
所以基本上,你需要保持对你创建的Timer
的引用,而不是仅使用局部变量。
答案 1 :(得分:1)
您传入的布尔值表示创建的线程是否为daemon
。如果它是守护进程,一旦所有非守护进程线程完成,线程将被停止。由于在您的应用程序中运行的唯一非守护程序线程是主线程,因此在main方法完成后它将立即停止。
正如Jon Skeet所说,如果没有活动线程引用Timer并且任务完成,则会完成一些完成操作,但如果它的守护进程和main方法完成,它可能无法正常退出。继续文档
......但是,这可能会发生任意长的时间。默认情况下,任务执行线程不作为守护程序线程运行,因此它能够阻止应用程序终止。如果呼叫者想要快速终止定时器的任务执行线程,则呼叫者应该调用定时器的取消方法。
回答你的问题
我能想到的唯一解释是CloseTask对象抛出未捕获的异常。那是对的吗?
如果JVM杀死非守护程序线程,它不会抛出任何异常。所以你真的不知道它发生了。