2016-07-17 2 views
2

У меня вопрос о CompletableFuture и его возможное использование для ленивых вычислений.Java 8 CompletedFuture ленивый контроль вычислений

Кажется, что это отличная замена для RunnableFuture для этой задачи, поскольку можно легко создать цепочки задач и полностью контролировать каждую цепочку ссылок. Тем не менее я обнаружил, что очень сложно контролировать, когда именно происходит вычисление.

Если я просто создаю CompletableFuture с помощью метода supplyAssync или что-то в этом роде, все в порядке. Он терпеливо ждет, когда я вызову метод get или join. Но если я попытаюсь сделать реальную цепочку с whenCompose, handle или любым другим способом, оценка начинается немедленно, что очень расстраивает.

Конечно, я всегда могу поставить задачу блокатора в начале цепочки и отпустить блок, когда я готов начать вычисление, но это кажется немного уродливым решением. Кто-нибудь знает, как управлять, когда действительно выполняется CompletableFuture.

ответ

3

Существует принципиальная разница между RunnableFuture и CompletableFuture, что вам здесь не хватает.

  • RunnableFuture реализации принимают задачу в качестве входных данных и удерживают ее. Он выполняет задачу, когда вы вызываете метод run.
  • A CompletableFuture не выполняет задачу. Он знает только о результате задачи. Он имеет три состояния: полный, неполный и завершено исключительно (не удалось).

CompletableFuture.supplyAsync является заводским методом, который дает вам незавершенный CompletableFuture. Он также планирует задачу, которая, когда она будет завершена, передаст ее результат в метод CompletableFuturecomplete. Другими словами, будущее, которое дает вам supplyAsync, что вы ничего не знаете о задаче и не можете контролировать выполнение задачи.

Чтобы использовать CompletableFuture так, как вы описали, вы должны создать подкласс:

public class RunnableCompletableFuture<T> extends CompletableFuture<T> implements RunnableFuture<T> { 
    private final Callable<T> task; 

    public RunnableCompletableFuture(Callable<T> task) { 
     this.task = task; 
    } 

    @Override 
    public void run() { 
     try { 
      complete(task.call()); 
     } catch (Exception e) { 
      completeExceptionally(e); 
     } 
    } 
} 
+0

Проблема не в том, чтобы запускать 'CompletingFuture', когда она мне нужна, но чтобы она не запускалась, когда мне это не нужно. Ваш пример делает то же самое, что я пытаюсь сделать сейчас (так как я не знаю, как это сделать лучше), а именно, поставить триггер в начале цепочки. – Darksnake

+1

Простите, если я не понял. Сам по себе «CompletableFuture» не «запускается». Это фабричный метод 'supplyAsync', который запускает задачу. Если вы используете метод 'supplyAsync', вы не можете управлять при запуске задачи. В моем ответе я пытаюсь предложить метод создания «CompletableFuture» из задачи без использования 'supplyAsync'. – Sam

+1

Я действительно понимаю, я просто надеялся, что есть лучшее решение. В настоящее время я просто создал класс контроллера, который содержит пустой 'CompletableFuture', который должен быть размещен в начале цепочки. Теперь, чтобы начать цепочку, мне нужно просто «закончить» ее. Также я могу отменить всю цепочку до ее начала. – Darksnake

0

Простой способ борьбы с вашей проблемой оборачивает ваш CompletableFuture в чем-то с ленивой природой. Вы можете использовать Поставщик или даже Java 8 Stream.

0

CompletedFuture - это push-дизайн, то есть результаты подталкиваются к зависимым задачам, как только они становятся доступными. Это также означает, что боковые цепи, которые сами по себе не потребляются, все равно выполняются, что может иметь побочные эффекты.

Что вы хотите - это тянущий дизайн, где предки будут втягиваться только по мере того, как их данные будут потребляться. Это будет принципиально иной дизайн, потому что побочных эффектов не потребляемых деревьев никогда не произойдет.

Конечно, с достаточным количеством искажений CF можно сделать так, как вы хотите, но вместо этого вы должны заглянуть в каркас fork-join, который позволяет вам выполнять только вычисления, на которых вы зависите, вместо того, чтобы сбрасывать результаты.

+0

Действительно, мне нужен тянущий дизайн, но я не знаю, где его получить. В настоящее время я не хочу писать всю инфраструктуру исполнения самостоятельно, поэтому я пытаюсь использовать существующие классы. В будущем я, вероятно, подумаю об этом. – Darksnake