【问题标题】:How to Chain multiple methods where each methods main thread has passed while inner thread is still working?如何链接多个方法,其中每个方法主线程已经通过而内部线程仍在工作?
【发布时间】:2016-06-11 19:26:03
【问题描述】:

我有这样的代码。每个方法都是一个独立暴露于 rest 调用的阶段:

void methodA(@FormDataParam ("fd") fd){
//Executor spawns thread - takes around 15-30 min as per data
//main thread exits with "Process begun" message while thread spawned still running
}

void methodB(){
//Executor spawns thread - takes around 60-600 min as per data
//main thread exits with "Process begun" message while thread spawned still running
}

void methodC(){
//Executor spawns thread - takes around 10-60 min as per data
//main thread exits with "Process begun" message while thread spawned still running
}

这些阶段之前是按顺序执行的。现在,如果需要,额外的要求是一次性执行它们。为此,必须将单个 api 公开为 rest api,例如:

void methodAll(@FormDataParam ("fd") fd){
methodA(fd);
methodB();
methodC();
}

问题是 methodA()、methodB()、methodC() 主线程立即返回,而它们产生的线程仍在运行,因此导致其他后续方法在前一个方法完成之前开始执行。

我正在修复中,如何在不对现有三个 api 进行实质性更改的情况下解决此问题?

【问题讨论】:

  • 一种方式——使用并传入回调
  • MetgodB 需要方法 A 的结果才能完成工作?
  • 最好的方法是重构代码,使这三个方法不返回 void 并且不执行线程。相反,它们只是执行它们的操作并返回它们的结果,然后在单个后台线程中连续调用这三个。
  • @HovercraftFullOfEels 感谢您的调查。是的,重构是您建议的一种方式,但我将其保留为最后一个选项,因为它阻碍了当前独立运行的执行模式。如果我没有得到更好的解决方案,我会继续。
  • @djxak - 没有朋友。 MethodB 不需要来自 MethodA 的输入,但由于这些是生命周期阶段,methodA 必须在 methodB 之前完成。实际上,当前 methodA 将其状态写入 DB 表中。如果没有对应于 methodA 的状态完成,则 methodB 永远不会启动。因此,一种方法是在特定时间的睡眠状态下循环,但我对其他更好的解决方案持开放态度。

标签: java multithreading rest


【解决方案1】:

现在你有了(伪代码):

class RestController {

  void methodA() {
    new Thread() {
      // some hard work
    }.start();
  }

  void methodB() {
    new Thread() {
      // some hard work
    }.start();
  }

}

重构后:

class RestController {

  void methodA() {
    new Thread() {
      service.methodA();
    }.start();
  }

  void methodB() {
    new Thread() {
      service.methodB();
    }.start();
  }

  void methodD() {
    new Thread() {
      service.methodA();
      service.methodB();
    }.start();
  }

}

class Service {

  void methodA() {
    // some hard work
  }

  void methodB() {
    // some hard work
  }

}

当然,您可以使用ExecutorService 或任何其他帮助程序进行线程化,而不是手动生成线程。

如果您的控制器在生成线程之前/之后进行验证、预处理和后处理,正如您所说,并且它实际上是相同的代码,那么您可以将其提取到采用 Runnable 并在 pre- 和 postProcess 之间执行的方法中避免重复。

  void execute(Runnable runnable) {
    // do validation
    // do preProcess
    new Thread(runnable).start();
    // do postProcess
  }
...
  void methodA() {
    execute(new Runnable() {
      @Override
      void run() {
        service.methodA();
      }
    });
  }

或者您可以在您的服务方法中进行此验证。

【讨论】:

  • 这就是问题所在。如果我调用 service.methodA();从 RestController 新方法 executeALL 然后我将错过验证和处理。如果我在 RestCONtroller 中调用执行方法,那么我不知道产生的线程是否已经完成。
  • 我使用上面代码中的想法来实现我的。我们正在考虑将完整代码重组为使用 CompletableFuture。
  • 我们正在考虑将完整代码重构为使用 CompletableFuture。但是,我从您的建议中吸取了教训,有效的是:以另一种方法复制现有 api 的完整代码,该方法具有额外的布尔输入,如“toChain”。虽然从 executeall tochain 调用将是 true,而从单个 api 调用它将是 false。最后添加新方法(显然在执行程序关闭之后) - 如果 toChain 为真,则使用 executor.awaittermination (给予等待的时间段足够高)以等待所有线程完成和单个 api 的执行程序完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-06
  • 1970-01-01
相关资源
最近更新 更多