основной поток не ждет CompletableFuture.runAsyn c () и вернул ответ - PullRequest
1 голос
/ 12 июля 2020

У меня есть функция, у которой есть метод invoke, который изнутри вызывает soap API, и для ее выполнения требуется около 22 секунд, в коде также есть несколько других методов, так что полностью deleteSoemthing () (код ниже) занимает примерно 24 секунды,

сейчас, я попытался запустить метод учета времени в отдельном потоке, поэтому я предполагал, что, хотя это отдельный поток, он будет просто оптимизирован до 2 секунд, потому что он занимал 22 секунды с всего 24 секунды.

поэтому вместо 24 секунд это может занять 22 секунды, потому что он работает параллельно.

но когда я запускаю это через почтальона, для выполнения требуется всего 2 секунды, я означает, что ответ возвращается через 2 секунды. и отдельный поток продолжает работать (когда я проверяю отладку).

Итак, я сомневаюсь, что основной поток не дожидается завершения этой задачи и не отправляет ответ обратно. или просто отправьте ответ и продолжайте запускать задачу asyn c в фоновом режиме

void deleteSomething(){

CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
                try {
                    invoke("invoking a soap API"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

//some other code

}

Ответы [ 2 ]

1 голос
/ 12 июля 2020

Это ожидаемое поведение CompletableFuture, если вы проверили документацию, в ней говорится -

/**
 * Returns a new CompletableFuture that is asynchronously completed
 * by a task running in the ForkJoinPool#commonPool() after
 * it runs the given action.
 *
 * @param runnable the action to run before completing the
 * returned CompletableFuture
 * @return the new CompletableFuture
 */

Вы можете использовать блокировку Future.get () для достижения желаемого (как показано ниже)

void deleteSomething(){
    ExecutorService executorService = Executors.newCachedThreadPool();

    Future<Void> future = executorService.submit(() -> {
        invoke("Invoking soap API");
        return null;
    });
    
    //some other code

    future.get();
}

Создание пула потоков в методе не рекомендуется, так как есть накладные расходы, связанные с созданием потока. В идеале пул потоков должен создаваться при запуске приложения.

1 голос
/ 12 июля 2020

Если вы хотите, чтобы основной поток (запрос) обрабатывал «какой-то другой код» и «запускал SOAP API» параллельно, а затем объединял и возвращал ответ конечному пользователю, то это не сработает.

Когда мы создаем экземпляр CompletableFuture, он запускает вычисления в другом потоке и немедленно возвращает Future. Если вам нужно заблокировать результат, вам нужно вызвать для него метод get . Однако этот процесс все равно займет 22 + 2 = 24 секунды, чтобы вернуть ответ.

Чтобы запустить две задачи параллельно, вы должны создать два Callable (s) и отправить их на ExecutorService

например,

  public void deleteSomething(){
    ExecutorService executorService = Executors.newFixedThreadPool(2);
    Collection<Callable<Void>> callables = new ArrayList<>();
    callables.add(() -> doSomeOtherTask());
    callables.add(() -> invokeSoapApi());
    try {
      List<Future<Void>> taskFutureList = executorService.invokeAll(callables);
      taskFutureList.get(0).get();
      taskFutureList.get(1).get();
    } catch (InterruptedException | ExecutionException e) {
      //error
    }
  }

  public Void doSomeOtherTask() {
    //some other code
    return null;
  }

  public Void invokeSoapApi() {
    //soap api call
    return null;
  }

Обратите внимание, что пул потоков должен создаваться при запуске приложения. Итак, если вы действительно используете wi sh для его использования, тогда у вас должен быть определен «executorService» как переменная экземпляра. например,

@Service
public class MyService {

  ...
  ...
  private ExecutorService executorService = Executors.newFixedThreadPool(2);
  ...
  ...
  //your methods
}

...