【问题标题】:How to call a method in parallel which returns the future?如何并行调用返回未来的方法?
【发布时间】:2023-04-03 09:27:02
【问题描述】:

我有一个如下所示的异步方法,它调用我的 Task 类,我的 Task 类完成所有工作。

    @Override
    public Future<DataResponse> executeAsync(DataKey key) {
        Future<DataResponse> future = null;

        try {
            Task task = new Task(key, restTemplate);
            future = executor.submit(task); 
        } catch (Exception ex) {
            // logging exception here
        }

        return future;
    }

下面是我的 Task 类,它完成了所有工作:

public class Task implements Callable<DataResponse> {

    private DataKey key;
    private RestTemplate restTemplate;

    public Task(DataKey key, RestTemplate restTemplate) {
        this.key = key;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() throws Exception {
        // some code here
    }
}

现在我需要并行调用executeAsync 方法,然后创建一个List&lt;DataResponse&gt; 对象并返回它。

@Override
public List<DataResponse> executeSync(DataKey key) {
    List<DataResponse> responseList = new ArrayList<DataResponse>();

    // make a List of DataKey using single key passed to this method.       
    List<DataKey> keys = new ArrayList<DataKey>();

    for(DataKey key : keys) {


    }
}

如何并行调用executeAsync 方法并返回responseList?在我的keys 列表中,最多有六个DataKey 对象。

【问题讨论】:

    标签: java concurrency future executorservice callable


    【解决方案1】:

    如果您希望返回包含由Task#call 返回的DataResponse 对象的List&lt;DataResponse&gt;,则不能异步执行此操作。您需要在 executeSync 内部进行阻塞以等待所有期货的结果。

    List<Future> futures = new ArrayList<>(keys.size());
    for(DataKey key : keys) {
        Future<DataResponse> future = executeAsync(key);
        futures.add(future);
    }
    
    for (Future<DataResponse> future : futures) {
        try {
            responseList.add(future.get());
        } catch (Exception e) {
            // do something else.
        }
        return responseList
    }
    

    使用Future 的更合适的解决方案是使用CompletionService,详见here

    在 Java 8 中,您应该使用 CompletableFuture(或 Guava 的 ListenableFuture)来执行异步任务。您仍然可以执行我上面所做的操作,或者您可以更改代码以充分利用延续任务。

    【讨论】:

    • 所以在这个例子中,假设我有 3 个期货,然后我们在 for 循环中迭代它们,在 try 块中它会为第一个未来调用 future.get,然后它会等待一次完成了,它会调用第二个未来对吗?
    • 有没有办法一次调用所有的future get 方法?我还在使用 Java 7。
    • 没错。 get 是一个阻塞调用。 CompletionService 使这更好一点。 一次性调用它们将是 akki 的解决方案,但您必须更改代码中的某些内容,它实际上在内部执行相同的操作。
    • 当你在内部说同样的话时,只是试图在下面这意味着什么?所以看起来好像没有办法并行调用所有future的get方法?
    • @david 看invokeAll的源码。您可以并行调用它们,但这可能比它的价值更麻烦。无论如何,你总是不得不在某个地方屏蔽。
    【解决方案2】:

    您可以调用 Executor 服务的 invokeAll 方法并将任务列表提交给它。 Executor Service 将并行执行任务。

    https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#invokeAll(java.util.Collection)

    这将与以下内容相同: 1. 在 executeSync 方法中从 for 循环中并行调用 executeAsync。 2. 在executeSync方法中从for循环依次调用executeAsync方法。

    【讨论】:

    • 所以这意味着我所有的期货 get 方法都将被并行调用?如果可能的话,你能举个例子吗?
    • @david 不,它们不会被并行调用。电话只是对您隐藏。
    【解决方案3】:

    没有理由并行执行它,因为它在内部已经并行运行。您唯一需要的是为每个项目调用异步方法并将生成的Futures 存储在列表中。在此循环结束后,您需要遍历那些 Futures 的列表,并在每个上面调用 get() 给出结果。这些结果列表您将作为返回值返回。

    【讨论】:

    • 所以当我们在每个未来调用 get 时,那将是顺序调用,对吗?我的意思是依次调用第一个未来,然后是第二个未来,然后是第三个未来。有什么方法可以一次调用所有的期货 get 方法?
    • 它只能是顺序的,因为您将结果附加到列表中,该列表是必须按顺序访问的数据结构。但是这个操作非常快,因为它只是检查未来是否已经完成并立即返回。一旦您调用 get,执行通常已经完成。
    • 所以等等,你的意思是说,一旦我将所有期货添加到列表中并且在我调用第一个未来之前它可能已经完成了?如果是真的,如果可能的话,你能解释一下吗?还在学习,所以对我有帮助。
    • 是的。您提交给 ExecutorService 的任务在不同的线程中运行并立即启动(或者一旦某些 ExecutorService 线程可用)。
    • 我明白了,所以SotiriosDelimanolis 给出的建议会起作用,而且它的作用与您的建议相同吗?
    猜你喜欢
    • 2020-07-14
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-18
    • 2017-05-07
    • 1970-01-01
    相关资源
    最近更新 更多