многопоточность с Future

у меня самый долгий поток не даёт вывести отработавшие фьючерсы, допустим я запускаю метот test в 4 потока первых 3 зависают на 1 сек, а 4й на 10сек и вот пока 4 поток не отработает фьючер ничего не выведет, хотя следующие три потока заполнит работой.

Вывод:

sleep: 1000
sleep: 1000
sleep: 1000
sleep: 10000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 10000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000

а мне бы хотелось узнать, как это можно исправить на такой результат:

sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 1000
sleep: 10000
sleep: 10000

Немного кода:

public static void main(String[] args) {
    TEEE testClass = TEEE.getInstance();
    ExecutorService executorService = Executors.newFixedThreadPool(4);
    List<Future> futures = new ArrayList<>();
    List<Integer> integers = new ArrayList<>();
    integers.add(1000);
    integers.add(1000);
    integers.add(1000);
    integers.add(10000);
    integers.add(1000);
    integers.add(1000);
    integers.add(1000);
    integers.add(1000);
    integers.add(10000);
    integers.add(1000);
    integers.add(1000);
    integers.add(1000);
    integers.add(1000);
    integers.forEach(x -> futures.add(executorService.submit(() -> testClass.test(x))));
    futures.forEach(x -> {
        try {
            System.out.println("sleep: " + x.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    });
}
public class TEEE {
   private static TEEE instance;
   public Integer test(Integer in) {
     try {
        Thread.sleep(Long.valueOf(in));

     } catch (InterruptedException e) {
        e.printStackTrace();
     }
     return in;
    }
   public static TEEE getInstance() {
    if (instance == null) {
        instance = new TEEE();
    }
    return instance;
}

}


Ответы (1 шт):

Автор решения: Roman Konoval

Проблема в том, что вы в цикле пробуете получить результат выполнения Future с помощью блокирующего вызова get. Он ждет, когда результат будет готов.

Нужно либо делать неблокирующий вызов либо проверять завершилась ли задача, перед тем как пробовать читать результат.

Но лучше всего воспользоваться CompletionService.

Он позволяет завернуть обычный Executor и ему так же как и обычному executor-у можно давать задачи, но при этом можно ждать когда следующая задача завершилась

ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletionService <Integer> completionService = 
       new ExecutorCompletionService<Integer>(executorService);

completionService.sumbit(() -> ...);
completionService.sumbit(() -> ...);


Future<Integer> resultFuture;

# poll блокируется пока не будет готов результат
while ((resultFuture = completionService.poll()) != null) {
  try {            
    System.out.println("sleep: " + resultFuture.get());
  } catch (InterruptedException e) {
    e.printStackTrace();
  } catch (ExecutionException e) {
     e.printStackTrace();
  }
};

→ Ссылка