1

我正在为Cay S. Horstmann编写的书籍“真正不耐烦的Java SE 8”进行练习。Java 8 CompletedFuture和ThreadLocalRandom

写方法

public static <T> CompletableFuture<T> repeat(
     Supplier<T> action, Predicate<T> until) 

异步重复动作,直到它产生 由until功能,这也应该运行 异步接受的值。使用从控制台读取 java.net.PasswordAuthentication的功能进行测试,并使用功能 ,该功能通过睡眠一秒来模拟有效性检查,然后通过 检查密码是“秘密”。

我想出了下面的代码,但随机密码生成策略似乎失败了我。所有的线程都会选择相同的密码,这看起来很奇怪。

public static <T> CompletableFuture<T> repeat(final Supplier<T> action, final Predicate<T> until) { 
    final CompletableFuture<T> futureAction = supplyAsync(action); 
    final boolean isMatchFound = futureAction.thenApplyAsync(until::test).join(); 

    final T suppliedValue = getSuppliedValue(futureAction); 

    if (isMatchFound) { 
     LOGGER.info("Got a match for value {}.", suppliedValue); 

     return futureAction; 
    } 

    return repeat(() -> suppliedValue, until); 
} 

private static <T> T getSuppliedValue(final CompletableFuture<T> futureAction) { 
    try { 
     return futureAction.get(); 
    } catch (InterruptedException | ExecutionException e) { 
     LOGGER.error(e.getMessage()); 
    } 

    return null; 
} 

测试用例:

@Test 
public void testRepeat() { 
    Supplier<PasswordAuthentication> action =() -> { 
     final String[] passwordVault = new String[] { "password", "secret", 
      "secretPassword" }; 

     final int len = passwordVault.length; 

     return new PasswordAuthentication("mickeyMouse", 
      passwordVault[ThreadLocalRandom.current().nextInt(len)].toCharArray()); 
    }; 
    @SuppressWarnings("static-access") 
    Predicate<PasswordAuthentication> until = passwordAuth -> { 
     try { 
      currentThread().sleep(1000); 
     } catch (InterruptedException e) { 
      fail(e.getMessage()); 
     } 

     final String password = String.valueOf(passwordAuth.getPassword()); 

     LOGGER.info("Received password: {}.", password); 

     return password.equals("secret"); 
    }; 

    repeat(action, until); 
} 

一个运行,看到相同的密码是如何奇怪拾取:

2015年1月9日15:41:33.350 [线程1 ] [INFO] najjcPracticeQuestionsCh6Test - 收到密码: secretPassword。 2015-01-09 15:41:34.371 [Thread-3] [INFO] n.a.j.j.c.PracticeQuestionsCh6Test - 收到密码: secretPassword。 2015-01-09 15:41:35.442 [Thread-5] [INFO] n.a.j.j.c.PracticeQuestionsCh6Test - 收到密码: secretPassword。 2015-01-09 15:41:36.443 [Thread-7] [INFO] n.a.j.j.c.PracticeQuestionsCh6Test - 收到密码: secretPassword。 2015-01-09 15:41:37.451 [Thread-9] [INFO] n.a.j.j.c.PracticeQuestionsCh6Test - 收到密码: secretPassword。

回答

0

我发现了这个错误。我创建的供应商每次都返回相同的值。以下是更正后的代码。但有一个问题:为什么编译器强制转换?

public static <T> CompletableFuture<T> repeat(final Supplier<T> action, 
     final Predicate<T> until) { 
    final CompletableFuture<T> futureAction = supplyAsync(action); 

    @SuppressWarnings("unchecked") 
    CompletableFuture<T> future = (CompletableFuture<T>) futureAction 
     .thenApplyAsync(until::test).thenApply(
      isMatchFound -> isMatchFound ? futureAction : repeat(action, until)); 

    future.join(); 

    return future; 

} 
+0

因为代码,因为你写,创建一个'CompletableFuture >'而不是'CompletableFuture '。链中的最后一个调用应该是['thenComposeAsync']](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#thenComposeAsync-java.util.function。函数 - )而不是'thenApply'。但是你的代码仍然是异步的,但并不是平行的,因为在前一阶段完成之后,每一步都被触发。因此,如果你想并行执行,你应该使用'Stream.generate(action).parallel()。filter(until).findFirst()'... – Holger 2015-01-10 12:53:13

+0

@Holger是的,并不是每一步都是异步的,问题陈述并没有问为了那个原因。它只是要求'as'直到'action'异步,我希望在'until'前使用'thenApplyAsync'来实现。 为什么你认为'thenComposeAsync'比'thenApply'更好的选择作为管道中的最后一步?我不需要最后一步是异步,所以即使我使用撰写,我会去'thenCompose',但我很好奇你为什么选择撰写申请。 – 2015-01-10 18:10:19

+0

@Holger如果我使用撰写,则返回类型是'CompletableFuture '而不是'>'。这也解释了演员。然而,阅读Javadoc,似乎撰写接受**前一阶段**作为参数,而不是前一阶段的**结果**。然而,这似乎并没有在实践中发生 - 它似乎正在接受布尔结果,而不是'CompletableFuture '。 _“返回一个新的CompletionStage,当这个阶段正常完成时,以该阶段作为所提供函数的参数执行。”_ – 2015-01-10 18:34:14

1

我认为你正在走下一条不必要的复杂路径。由于供应 - 测试 - 供应 - 测试 - 供应 - 测试序列是连续的,所以您需要初始的supplyAsync。没有理由在异步中执行异步。

这里有一个简单的实现:

public static <T> CompletableFuture<T> repeat(final Supplier<T> action, 
     final Predicate<T> until) { 

    return CompletableFuture.supplyAsync(() -> 
     Stream.generate(action) 
       .filter(until) 
       .findFirst() 
       .get() 
    ); 
} 
+0

我不认为这符合'until'函数也应该异步运行的要求。看到我上面的帖子中的问题陈述。 – 2015-01-10 07:39:53

+0

@Abhijit Sarkar:['Stream.parallel()'](http://docs.oracle.com/javase/8/docs/api/java/util/stream/BaseStream.html#parallel--)... – Holger 2015-01-10 12:45:55