9.5 Future
构建复杂并行操作的另外一种方案是使用Future。Future像一张欠条,方法不是返回一个值,而是返回一个Future对象,该对象第一次创建时没有值,但以后能拿它“换回”一个值。
调用Future对象的get方法获取值,它会阻塞当前线程,直到返回值。可惜,和回调一样,组合Future对象时也有问题,我们会快速浏览这些可能碰到的问题。
我们要考虑的场景是从外部网站查找某专辑的信息。我们需要找出专辑上的曲目列表和艺术家,还要保证有足够的权限访问登录等各项服务,或者至少确保已经登录。
例9-9使用Future API解决了该问题。在➊处登录提供曲目和艺术家信息的服务,这时会返回一个Future对象,该对象包含登录信息。Future接口支持泛型,可将Future看作是Credentials对象的一张欠条。
例9-9 使用Future从外部网站下载专辑信息
@Overridepublic Album lookupByName(String albumName) {Future<Credentials> trackLogin = loginTo("track"); ➊Future<Credentials> artistLogin = loginTo("artist");try {Future<List<Track>> tracks = lookupTracks(albumName, trackLogin.get()); ➋Future<List<Artist>> artists = lookupArtists(albumName, artistLogin.get());return new Album(albumName, tracks.get(), artists.get()); ➌} catch (InterruptedException | ExecutionException e) {throw new AlbumLookupException(e.getCause()); ➍}}
在➋处使用登录后的凭证查询曲目和艺术家信息,通过调用Future对象的get方法获取凭证信息。在➌处构建待返回的专辑对象,这里同样调用get方法以阻塞Future对象。如果有异常,我们在➍处将其转化为一个待解问题域内的异常,然后将其抛出。
读者将会看到,如果要将Future对象的结果传给其他任务,会阻塞当前线程的执行。这会成为一个性能问题,任务不是平行执行了,而是(意外地)串行执行。
以例9-9来说,这意味着在登录两个服务之前,我们无法启动任何查找任务。没必要这样:lookupTracks只需要自己的登录凭证,lookupArtists也是一样。我们将理想的行为用图9-3描述出来。

图9-3:查询操作不必等待所有登录操作完成后才能执行
可以将对get的调用放到lookupTracks和lookupArtists方法的中间,这能解决问题,但是代码丑陋,而且无法在多次调用之间重用登录凭证。
我们真正需要的是不必调用get方法阻塞当前线程,就能操作Future对象返回的结果。我们需要将Future和回调结合起来使用。
