直接从CompletableFuture.thenAccept返回值

spa*_*pan 3 java concurrency java-8 completable-future

我试图从我CompletableFuture这样的列表中返回一个列表:

public List<Provider> get() {
    CompletableFuture<List<Provider>> providersResponse = getSomeData();
    return providersResponse.thenAccept((List<Provider> providers) -> {
        return providers;
    });
}
Run Code Online (Sandbox Code Playgroud)

它失败了"意外的返回类型.但是如何以异步方式返回结果?

Hol*_*ger 6

你的目标存在根本矛盾.您只能拥有,get()返回一个完整的,可直接使用的列表或"以异步方式返回结果".

如果该方法List<Provider> get()应该返回一个List可以被调用者无限制地使用的方法,则它不能保持异步操作,因为操作必须在get()返回时完成.这可以像调用一样简单join(),等待完成并获得结果:

public List<Provider> get() {
    CompletableFuture<List<Provider>> providersResponse = getSomeData();
    return providersResponse.join();
}
Run Code Online (Sandbox Code Playgroud)

要不就

public List<Provider> get() {
    return getSomeData().join();
}
Run Code Online (Sandbox Code Playgroud)

这有效地将潜在的异步操作getSomeData()转变为同步操作.

这个答案,使用

public List<Provider> get() {
    List<Provider> providers = new ArrayList<>();
    CompletableFuture<List<Provider>> providersResponse = getSomeData();
    providersResponse.thenAccept(providers::addAll);
    return providers;
}
Run Code Online (Sandbox Code Playgroud)

不等待操作完成,但ArrayList在调度addAll操作后返回一个新操作,该操作的执行完全不受此方法的控制.

这是一个异步操作,因此在get()返回时,List可能仍然为空,但稍后由任意线程更新.由于ArrayList不是线程安全的,感知状态不必是,空的或完成的,它可以是任意的中间状态,甚至不需要是一致的.在使用该代码时,要为奇怪的异常,不可能看的情况或其他惊喜做好准备.

当您使用线程安全List实现修复该代码时,您仍然会遇到以下问题:List在查询时返回的可能为空,并且在调用者控件之外的任意时间点填充.这是不可修复的,正如开头所说,这是想要异步操作但返回a的基本问题List.

CompletableFuture已经是潜在异步操作的封装,因此如果您希望操作保持异步,只需返回CompletableFuture<List<Provider>>调用者,以便获得控制权.否则,如果您希望调用者接收List可以无限制使用的调用者,请在返回结果之前等待完成,例如通过join().

  • @inquisitive 是的,这意味着“允许调用者获得控制权”。调用者可以决定同时执行一些其他操作,但调用者可以在需要该值时调用“join()”。或者,调用者可以链接其他相关操作,以便在完成时执行。 (2认同)