我正在为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。
因为代码,因为你写,创建一个'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
@Holger是的,并不是每一步都是异步的,问题陈述并没有问为了那个原因。它只是要求'as'直到'action'异步,我希望在'until'前使用'thenApplyAsync'来实现。 为什么你认为'thenComposeAsync'比'thenApply'更好的选择作为管道中的最后一步?我不需要最后一步是异步,所以即使我使用撰写,我会去'thenCompose',但我很好奇你为什么选择撰写申请。 – 2015-01-10 18:10:19
@Holger如果我使用撰写,则返回类型是'CompletableFuture'而不是'>'。这也解释了演员。然而,阅读Javadoc,似乎撰写接受**前一阶段**作为参数,而不是前一阶段的**结果**。然而,这似乎并没有在实践中发生 - 它似乎正在接受布尔结果,而不是'CompletableFuture '。 _“返回一个新的CompletionStage,当这个阶段正常完成时,以该阶段作为所提供函数的参数执行。”_ –
2015-01-10 18:34:14