应该永远运行的任务的 Java Executor 最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2104676/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java Executor Best Practices for Tasks that Should Run Forever
提问by BlairHippo
I'm working on a Java project where I need to have multiple tasks running asynchronously. I'm led to believe Executor is the best way for me to do this, so I'm familiarizing myself with it. (Yay getting paid to learn!) However, it's not clear to me what the best way is to accomplish what I'm trying to do.
我正在处理一个需要异步运行多个任务的 Java 项目。我相信 Executor 是我做到这一点的最佳方式,所以我正在熟悉它。(是的,为了学习而获得报酬!)但是,我不清楚完成我正在尝试做的事情的最佳方法是什么。
For the sake of argument, let's say I have two tasks running. Neither is expected to terminate, and both should run for the duration of the application's life. I'm trying to write a main wrapper class such that:
为了便于论证,假设我有两个任务正在运行。两者都不会终止,并且都应该在应用程序的生命周期内运行。我正在尝试编写一个主要的包装类,以便:
- If either task throws an exception, the wrapper will catch it and restart the task.
- If either task runs to completion, the wrapper will notice and restart the task.
- 如果任一任务抛出异常,包装器将捕获它并重新启动任务。
- 如果任一任务运行完成,包装器将注意到并重新启动该任务。
Now, it should be noted that the implementation for both tasks will wrap the code in run()
in an infinite loop that will never run to completion, with a try/catch block that should handle all runtime exceptions without disrupting the loop. I'm trying to add another layer of certainty; if either I or somebody who follows me does something stupid that defeats these safeguards and halts the task, the application needs to react appropriately.
现在,应该注意的是,这两个任务的实现会将代码包装run()
在一个永远不会运行完成的无限循环中,并带有一个应在不中断循环的情况下处理所有运行时异常的 try/catch 块。我正在尝试添加另一层确定性;如果我或跟随我的人做了一些愚蠢的事情来破坏这些保护措施并停止任务,应用程序需要做出适当的反应。
Is there a best practice for approaching this problem that folks more experienced than me would recommend?
是否有解决这个问题的最佳实践,比我推荐的更有经验的人?
FWIW, I've whipped-up this test class:
FWIW,我已经完成了这个测试课程:
public class ExecTest {
private static ExecutorService executor = null;
private static Future results1 = null;
private static Future results2 = null;
public static void main(String[] args) {
executor = Executors.newFixedThreadPool(2);
while(true) {
try {
checkTasks();
Thread.sleep(1000);
}
catch (Exception e) {
System.err.println("Caught exception: " + e.getMessage());
}
}
}
private static void checkTasks() throws Exception{
if (results1 == null || results1.isDone() || results1.isCancelled()) {
results1 = executor.submit(new Test1());
}
if (results2 == null || results2.isDone() || results2.isCancelled()) {
results2 = executor.submit(new Test2());
}
}
}
class Test1 implements Runnable {
public void run() {
while(true) {
System.out.println("I'm test class 1");
try {Thread.sleep(1000);} catch (Exception e) {}
}
}
}
class Test2 implements Runnable {
public void run() {
while(true) {
System.out.println("I'm test class 2");
try {Thread.sleep(1000);} catch (Exception e) {}
}
}
}
It's behaving the way I want, but I don't know if there are any gotchas, inefficiencies, or downright wrong-headedness waiting to surprise me. (In fact, given that I'm new to this, I'd be shocked if there wasn'tsomething wrong/inadvisable about it.)
它以我想要的方式行事,但我不知道是否有任何陷阱、低效率或彻头彻尾的错误头脑等着让我大吃一惊。(事实上,鉴于我是新手,如果没有任何错误/不明智的地方,我会感到震惊。)
Any insight is welcomed.
欢迎任何见解。
采纳答案by Yoni
I faced a similar situation in my previous project, and after my code blew in the face of an angry customer, my buddies and I added two big safe-guards:
我在之前的项目中也遇到过类似的情况,当我的代码在一个愤怒的客户面前自爆后,我和小伙伴们加了两个大安全措施:
- In the infinite loop, catch Errors too, not just exceptions. Sometimes unexcepted things happen and Java throws an Error at you, not an Exception.
- Use a back-off switch, so if something goes wrong and is non-recoverable, you don't escalate the situation by eagerly starting another loop. Instead, you need to wait until the situation goes back to normal and then start again.
- 在无限循环中,也要捕获错误,而不仅仅是异常。有时会发生意外的事情,Java 会向您抛出错误,而不是异常。
- 使用退避开关,因此如果出现问题且不可恢复,您不会急切地启动另一个循环,从而使情况升级。相反,您需要等到情况恢复正常,然后重新开始。
For example, we had a situation where the database went down and during the loop an SQLException was thrown. The unfortunate result was that the code went through the loop again, only to hit the same exception again, and so forth. The logs showed that we hit the same SQLException about 300 times in a second!! ... this happened intermittently several times with occassional JVM pauses of 5 seconds or so, during which the application was not responsive, until eventually an Error was thrown and the thread died!
例如,我们遇到过数据库宕机并且在循环期间抛出 SQLException 的情况。不幸的结果是代码再次通过循环,只是再次遇到相同的异常,依此类推。日志显示我们在一秒钟内击中了大约 300 次相同的 SQLException!...这会间歇性地发生几次,偶尔会有 5 秒左右的 JVM 暂停,在此期间应用程序没有响应,直到最终抛出错误并且线程死亡!
So we implemented a back-off strategy, approximately shown in the code below, that if the exception is not recoverable (or is excepted to recover within a matter of minutes), then we wait for a longer time before resuming operations.
因此,我们实施了一个退避策略,大致如下面的代码所示,如果异常不可恢复(或在几分钟内被排除恢复),那么我们在恢复操作之前等待更长的时间。
class Test1 implements Runnable {
public void run() {
boolean backoff = false;
while(true) {
if (backoff) {
Thread.sleep (TIME_FOR_LONGER_BREAK);
backoff = false;
}
System.out.println("I'm test class 1");
try {
// do important stuff here, use database and other critical resources
}
catch (SqlException se) {
// code to delay the next loop
backoff = true;
}
catch (Exception e) {
}
catch (Throwable t) {
}
}
}
}
If you implement your tasks this way then I don't see a point in having a third "watch-dog" thread with the checkTasks() method. Furthermore, for the same reasons I outlined above, I'd be cautious to just start the task again with the executor. First you need to understand why the task failed and whether the environment is in a stable condition that running the task again would be useful.
如果您以这种方式实现您的任务,那么我认为使用 checkTasks() 方法设置第三个“看门狗”线程没有意义。此外,出于与我上面概述的相同的原因,我会谨慎地与执行程序再次启动任务。首先,您需要了解任务失败的原因以及环境是否处于稳定状态,再次运行该任务是否有用。
回答by David Sowsy
Aside to eyeballing it, I generally run Java code against static analysis tools like PMDand FindBugsto look for deeper issues.
除了观察它,我通常会针对PMD和FindBugs等静态分析工具运行 Java 代码以寻找更深层次的问题。
Specifically for this code FindBugs didn't like that results1 and results2 are not volatile in the lazy init, and that the run() methods might ignore the Exception because they aren't explicitly being handled.
特别是对于这段代码,FindBugs 不喜欢 result1 和 results2 在惰性初始化中不是易失性的,并且 run() 方法可能会忽略异常,因为它们没有被显式处理。
In general I am a bit leery of the use of Thread.sleepfor concurrency testing, preferring timers or terminating states/conditions. Callable might be useful in returning something in the event of a disruption that throws an exception if unable to compute a result.
一般来说,我对使用Thread.sleep进行并发测试、更喜欢计时器或终止状态/条件有点怀疑。Callable 可能有助于在发生中断时返回某些内容,如果无法计算结果则抛出异常。
For some best practices and more food for thought, check out Concurrency in Practice.
有关一些最佳实践和更多思考的信息,请查看并发实践。
回答by Jean-Philippe Caruana
have you tried Quartz framework?
你试过Quartz 框架吗?
回答by deepak
how about this
这个怎么样
Runnable task = () -> {
try{
// do the task steps here
} catch (Exception e){
Thread.sleep (TIME_FOR_LONGER_BREAK);
}
};
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(task,0, 0,TimeUnit.SECONDS);