Java Executor应该永远运行的任务的最佳实践 [英] Java Executor Best Practices for Tasks that Should Run Forever

查看:94
本文介绍了Java Executor应该永远运行的任务的最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个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(例外e){
System.err.println(Caught exception:+ e.getMessage());
}
}
}

private static void checkTasks()抛出异常{
if(results1 == null || results1.isDone()| | results1.isCancelled()){
results1 = executor.submit(new Test1());
}

if(results2 == null || results2.isDone()|| results2.isCancelled()){
results2 = executor.submit(new Test2()) ;
}
}
}

类Test1实现Runnable {
public void run(){
while(true){
System.out.println(我是测试类1);
try {Thread.sleep(1000);} catch(Exception e){}
}

}
}

class Test2实现Runnable {
public void run(){
while(true){
System.out.println(我是测试类2);
try {Thread.sleep(1000);} catch(Exception e){}
}
}
}

它表现得像我想要的那样,但我不知道是否有任何陷阱,效率低下或者彻头彻尾的错误头脑等着让我感到惊讶。 (事实上​​,鉴于我是新手,如果不是错误/不可取的话,我会感到震惊。)



欢迎任何见解。

解决方案

我在上一个项目中遇到过类似的情况,并且在我的代码爆炸之后面对愤怒的顾客,我的朋友和我添加了两个大保镖:


  1. 在无限循环中,捕捉错误,而不仅仅是例外。有时会发生未触发的事情,Java会向您抛出错误,而不是异常。

  2. 使用后退开关,因此如果出现问题并且无法恢复,则不会升级热切地开始另一个循环的情况。相反,你需要等到情况恢复正常然后重新开始。

例如,我们遇到的情况是数据库失效并在循环期间抛出了SQLException。不幸的结果是代码再次通过循环,只是再次遇到同样的异常,等等。日志显示我们在一秒内达到相同的SQLException大约300次!! ...这种情况间歇性地发生了几次,偶尔的JVM暂停时间为5秒左右,在此期间应用程序没有响应,直到最终抛出错误并且线程死亡!



<因此,我们实施了一个退避策略,大致在下面的代码中显示,如果异常不可恢复(或在几分钟内恢复除外),那么我们会在恢复操作之前等待更长的时间。 / p>

  class Test1实现Runnable {
public void run(){
boolean backoff = false;
while(true){
if(backoff){
Thread.sleep(TIME_FOR_LONGER_BREAK);
backoff = false;
}
System.out.println(我是测试类1);
try {
//在这里做重要的事情,使用数据库和其他关键资源
}
catch(SqlException se){
//延迟下一个循环的代码
backoff = true;
}
catch(例外e){
}
catch(Throwable t){
}
}
}
}

如果你以这种方式实现你的任务,那么我没有看到第三个手表的重点-dog使用checkTasks()方法的线程。此外,由于我在上面概述的相同原因,我会谨慎地再次与执行者一起开始任务。首先,您需要了解任务失败的原因以及环境是否处于稳定状态,再次运行任务会很有用。


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.

解决方案

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:

  1. In the infinite loop, catch Errors too, not just exceptions. Sometimes unexcepted things happen and Java throws an Error at you, not an Exception.
  2. 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.

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.

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.

这篇关于Java Executor应该永远运行的任务的最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆