我通常看到两种方法来实现一个守护进程,它做一些工作并进入睡眠并再次醒来。
while(flag) 方法,其中 flag 为 true,如果我们想停止守护程序,则由其他类设置为 false。
while(flag){ //do something Thread.sleep(10000l); }
使用固定延迟使用 ScheduledThreadPoolExecutor 进行调度。
IMO,第二种方法更清洁且易于测试。有人可以比较这两种方法。第一种方法会导致任何内存问题吗?
谢谢
我通常看到两种方法来实现一个守护进程,它做一些工作并进入睡眠并再次醒来。
while(flag) 方法,其中 flag 为 true,如果我们想停止守护程序,则由其他类设置为 false。
while(flag){
//do something
Thread.sleep(10000l);
}
使用固定延迟使用 ScheduledThreadPoolExecutor 进行调度。
IMO,第二种方法更清洁且易于测试。有人可以比较这两种方法。第一种方法会导致任何内存问题吗?
谢谢
第一种方法会导致任何内存问题吗?
不,最大的问题是管理flag
变量的可见性。每当我看到有人建议这种方法时,我都会立即投反对票。该类Thread
出于某种原因封装了中断标志的概念。
推荐方法 2)。
方法一对底层线程系统(尤其是在 UNIX 上)的随机唤醒不稳健,您还需要实现自己的错误处理
方法二允许您从底层抽象Thread
并使用Runnable
or Callable
。
此外,方法 1) 存在时钟漂移问题,即您的任务需要非零时间来执行,因此它不会每十秒执行一次。实际上,ScheduledExecutorService
它将每秒安排一次执行,或者,如果需要,以十秒为间隔。
方法 2) 提供了一种简单的方法来安排线程在一段时间内做某事,然后按照javadoc中的示例将其杀死。
最后,ExecutorSevice
关闭自定义线程要容易得多,只需调用executorService.shutdown()
然后executorService.awaitTermination()
等待最后一个任务完成即可。
您可能要注意的一件事是 javadoc 中的这个 gem -“如果任务的任何执行遇到异常,则后续执行将被抑制”。这意味着要么你必须非常小心try/catch
,Callable
要么你需要继承它ScheduledExecutorService
(取自 javadoc):
public class MyScheduledExecutor extends ScheduledThreadPoolExecutor {
public MyScheduledExecutor(int corePoolSize) {
super(corePoolSize);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null) {
System.out.println(t);
}
}
}