【问题标题】:Java Invoke Collection of Tasks (as with invokeAll)Java 调用任务集合(与 invokeAll 一样)
【发布时间】:2017-10-09 11:35:36
【问题描述】:

我正在寻找一种处理javafx.concurrent.Task 的方法,可以用java.util.concurrent.CallableExecutorService.invokeAll(Collection<? extends Callable<T>>) 完成什么此方法不能与Tasks 一起使用,因为它们是runnables 而不是callables。

使用Executors.callable(Runnable task) 将我的任务类型转换为Callable 不起作用,因为返回的Callable 将始终返回null,而我需要从Task.call() 返回的值。

我了解Task 并非设计用于Collections。但是我需要在我的 JavaFX 应用程序中使用 Task,并通过编写测试遇到了这个问题。

除了简单地让我的Task 实现Callable 之外,还有其他方法可以规避这个问题吗?我不想这样做,因为我会更改我的代码以适应我的测试。

【问题讨论】:

  • 您说的是RunnableCallable。重写你的问题,使它比现在更清楚。另外,解释一下让你的任务实现Callable有什么问题。
  • 我做了,你明白这件事了吗?
  • 是的,从这里bugs.openjdk.java.net/browse/JDK-8166449 我们可以看到实现Callable 并不是一个好主意。我稍微编辑了您的问题以突出差异。
  • 你不能把所有的任务都包装在一个Callable中,比如exec.invokeAll(tasks.stream().map(t -> () -> {t.run(); return t.getValue();}).collect(toList()));

标签: java javafx concurrency task executorservice


【解决方案1】:

您可以将每个任务包装在一个可调用对象中。基本上对于每个Task<T> task,您都需要一个Callable<T>,如下所示:

Callable<T> callable = () -> {
    task.run();
    return task.getValue();
};

因此,例如,您可以定义一个执行此映射的Function&lt;Task&lt;T&gt;, Callable&lt;T&gt;&gt;

Function<Task<T>, Callable<T>> taskWrapper = task -> () -> {
    task.run();
    return task.getValue();
};

然后给出ExecutorService execCollection&lt;Task&lt;T&gt;&gt; tasks 你可以这样做

List<Future<T>> results = exec.invokeAll(tasks.stream()
    .map(taskWrapper)
    .collect(Collectors.toList()));

这是一个 SSCCE:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class InvokeAllTasks extends Application {

    private Random rng = new Random();
    private ExecutorService exec = Executors.newFixedThreadPool(5);
    private Function<Task<Integer>, Callable<Integer>> taskWrapper = task -> () -> {
        task.run();
        return task.getValue();
    };

    @Override
    public void start(Stage primaryStage) {
        Button runAll = new Button("Run all tasks");
        Label status = new Label();

        runAll.setOnAction(e -> {
            List<Task<Integer>> tasks = createTasks();


            Task<List<Future<Integer>>> runAllTask = new Task<List<Future<Integer>>>() {
                @Override
                protected List<Future<Integer>> call() throws Exception {
                    return exec.invokeAll(tasks.stream().map(taskWrapper).collect(Collectors.toList()));
                }
            };
            status.setText("Running...");
            runAllTask.setOnSucceeded(evt -> status.setText("All Done"));
            new Thread(runAllTask).start();


        });

        VBox root = new VBox(5, runAll, status);
        root.setMinHeight(120);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    @Override
    public void stop() {
        exec.shutdown();
    }

    private List<Task<Integer>> createTasks() {
        List<Task<Integer>> tasks = new ArrayList<>();
        for (int i = 1 ; i <= 8 ; i++) {
            String name = "Task "+i;
            Task<Integer> t = new Task<Integer>() {
                @Override
                protected Integer call() throws Exception {
                    System.out.println(name+" running");
                    Thread.sleep(rng.nextInt(1000)+500);
                    int result = rng.nextInt(500);
                    System.out.println(name+" computed "+result);
                    return result;
                }
            };
            tasks.add(t);
        }
        return tasks;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

【讨论】:

  • 一段时间后我又读了一遍,因为它对我有很大帮助。不过有一件事我不明白。为什么标签状态设置为“全部完成”,正是在计算所有值时。
  • @J.Ober ExecutorService.invokeAll() 阻塞,直到传递给它的所有可调用对象都完成,所以 runAllTask 在所有单个任务完成时进入 SUCCEEDED 状态。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
  • 1970-01-01
相关资源
最近更新 更多