对于应该永远运行的任务的 Java 执行器最佳实践
我正在开发一个 Java 项目,其中需要异步运行多个任务。我相信 Executor 是我做到这一点的最佳方式,所以我正在熟悉它。 (是的,学习是有报酬的!)但是,我不清楚完成我想做的事情的最佳方法是什么。
为了便于讨论,假设我正在运行两个任务。两者都不会终止,并且都应该在应用程序的生命周期内运行。我正在尝试编写一个主包装类,以便:
- 如果任一任务抛出异常,包装器将捕获它并重新启动该任务。
- 如果任一任务运行完成,包装器将注意到并重新启动该任务。
现在,应该注意的是,这两个任务的实现都将把 run()
中的代码包装在一个永远不会运行完成的无限循环中,并使用一个应该处理所有运行时异常的 try/catch 块而不破坏循环。我正在尝试增加另一层确定性;如果我或跟随我的人做了一些愚蠢的事情,破坏了这些保护措施并停止了任务,则应用程序需要做出适当的反应。
是否有比我更有经验的人推荐的解决这个问题的最佳实践?
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) {}
}
}
}
它的行为符合我想要的方式,但我不知道是否有任何陷阱、效率低下或完全错误的头脑等待着让我惊讶。 (事实上,鉴于我对此不熟悉,如果其中没有任何错误/不可取的地方,我会感到震惊。)
欢迎任何见解。
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.
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.
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:
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't something wrong/inadvisable about it.)
Any insight is welcomed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我在之前的项目中也遇到过类似的情况,当我的代码在愤怒的客户面前崩溃后,我和我的伙伴们添加了两大安全措施:
例如,我们遇到过这样的情况:数据库出现故障,并且在循环期间抛出 SQLException。不幸的结果是代码再次执行循环,却再次遇到相同的异常,等等。日志显示我们每秒遇到相同的 SQLException 大约 300 次! ...这种情况间歇性地发生了几次,偶尔 JVM 会暂停 5 秒左右,在此期间应用程序没有响应,直到最终抛出错误并且线程终止!
因此,我们实施了一种退避策略,大致如下面的代码所示,如果异常不可恢复(或者异常在几分钟内恢复),那么我们会等待更长的时间才能恢复操作。
如果您以这种方式实现任务,那么我认为使用 checkTasks() 方法创建第三个“看门狗”线程没有意义。此外,出于与我上面概述的相同原因,我会谨慎地与执行者再次启动任务。首先,您需要了解任务失败的原因以及环境是否处于稳定状态,再次运行任务会有用。
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:
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!
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.
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.
除了目击之外,我通常会针对 PMD 等静态分析工具运行 Java 代码和 FindBugs 查找更深层次的问题。
特别是对于这段代码,FindBugs 不喜欢 results1 和 results2 在惰性初始化中不是易失性的,并且 run() 方法可能会忽略异常,因为它们没有被显式处理。
一般来说,我对 Thread.sleep 用于并发测试,首选计时器或终止状态/条件。如果无法计算结果而引发异常,Callable 可能有助于在发生中断时返回某些内容。
有关一些最佳实践和更多值得深思的内容,请查看并发实践。
Aside to eyeballing it, I generally run Java code against static analysis tools like PMD and FindBugs to look for deeper issues.
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.
In general I am a bit leery of the use of Thread.sleep for 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.
For some best practices and more food for thought, check out Concurrency in Practice.
这个怎么样
how about this
您尝试过 Quartz 框架 吗?
have you tried Quartz framework ?