1

我正在练习 Cay S. Horstmann 所著的“真正不耐烦的 Java SE 8”一书。

写一个方法

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

异步重复操作,直到它产生一个函数接受的值,该until函数也应该异步运行。使用从控制台读取 a 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-01-09 15:41:33.350 [Thread-1] [INFO] najjcPracticeQuestionsCh6Test - 收到密码:secretPassword。2015-01-09 15:41:34.371 [Thread-3] [INFO] najjcPracticeQuestionsCh6Test - 收到密码:secretPassword。2015-01-09 15:41:35.442 [Thread-5] [INFO] najjcPracticeQuestionsCh6Test - 收到密码:secretPassword。2015-01-09 15:41:36.443 [Thread-7] [INFO] najjcPracticeQuestionsCh6Test - 收到密码:secretPassword。2015-01-09 15:41:37.451 [Thread-9] [INFO] najjcPracticeQuestionsCh6Test - 收到密码:secretPassword。

4

2 回答 2

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()
    );
}
于 2015-01-10T01:05:57.463 回答
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;

}
于 2015-01-09T22:53:05.880 回答