CompletionStage.thenCompose不按顺序执行

时间:2017-09-20 21:04:14

标签: java java-8 completable-future

我正在尝试使用java 8 CompletionStages来串行执行2个异步方法,以便在第一个失败时不执行第二个异步方法。但是当我调用thenCompose时,传入的函数似乎在前一个函数完成之前开始(例如:两个函数错误地并行执行。这是代码:

  public CompletionStage<Graph> create(Payload payload) {
    CompletionStage<BlobInfo> fileFuture = createFile(payload);
    CompletionStage<Entity> metadataFuture = createMetadata(payload);
    return fileFuture
        .thenCompose(ignore -> metadataFuture)
        .thenApply(entity ->
            buildFromEntity(objectMapper, entity));
  }

  public CompletionStage<BlobInfo> createFile(Payload payload) {
    return CompletableFuture.supplyAsync(() -> {
      try {
        return
            storage.create(
                BlobInfo
                    .newBuilder(payload.bucket, payload.name)
                    .build(),
                payload.data.getBytes());
      } catch (StorageException e) {
        LOG.error("Failed to write to storage: " + e);
        throw new RequestHandlerException(StatusCode.SERVER_ERROR,
            "Failed to write to storage.");
      }
    });
  }


  public CompletionStage<Entity> createMetadata(Payload payload) {
    return CompletableFuture.supplyAsync(() -> createSync(payload));
  }

  private Entity createMetadataSync(Payload payload) {
    Key key = keyFactory.newKey(payload.id);
    Entity.Builder entityBuilder = GraphPayload.buildEntityFromGraph(payload, key);
    Entity entity = entityBuilder.build();
    LOG.error("Metadata.createSync");

    try {
      datastore.add(entity);
    } catch (DatastoreException e) {
      LOG.error("Failed to write initial metadata: " + e);
      throw new RequestHandlerException(StatusCode.SERVER_ERROR,
          "Failed to write initial metadata.");
    }
    return entity;
  }

输出:

16:57:47.530 [ForkJoinPool.commonPool-worker-3] ERROR com.spotify.nfgraphstore.store.FileStore - CreateFile
16:57:47.530 [ForkJoinPool.commonPool-worker-2] ERROR com.spotify.nfgraphstore.store.MetadataStore - Metadata.createSync
16:57:47.530 [ForkJoinPool.commonPool-worker-3] ERROR com.spotify.nfgraphstore.store.FileStore - Failed to write initial graph to storage: com.google.cloud.storage.StorageException: X

记录的输出表明在抛出Storage异常之前,Metadata.createSync正在执行。该结论也是由测试(未示出)产生的,如果对文件存储DB的写入失败,该测试应该显示与元数据DB的零交互。该测试有时会失败,表明存在竞争条件。

所以我一直在想,然后撰写并不能保证串行执行。但是我在java文档中读到的所有内容都表明执行应该是串行的:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html#thenCompose-java.util.function.Function-

有谁知道为什么执行不能保证是连续的,或者推荐其他功能可能更符合我的意图?

1 个答案:

答案 0 :(得分:5)

createMetadata的调用立即启动任务,因为它不是作为传递给thenCompose的lambda表达式的一部分调用的。

也许你打算这样做:

.thenCompose(ignore -> createMetadata(payload))