Java 8 CompletedFuture 和 ThreadLocalRandom
Java 8 CompletedFuture and ThreadLocalRandom
我正在练习 Cay S. Horstmann 的 "Java SE 8 for the Really Impatient" 一书中的练习。
Write a method
public static <T> CompletableFuture<T> repeat(
Supplier<T> action, Predicate<T> until)
that asynchronously repeats the action until it produces a value that
is accepted by the until
function, which should also run
asynchronously. Test with a function that reads a
java.net.PasswordAuthentication
from the console, and a function
that simulates a validity check by sleeping for a second and then
checking that the password is "secret".
我想出了以下代码,但随机密码生成策略似乎让我失望了。所有线程不断地选择相同的密码,这看起来很奇怪。
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 ]
n.a.j.j.c.PracticeQuestionsCh6Test - Received password:
secretPassword. 2015-01-09 15:41:34.371 [Thread-3] [INFO ]
n.a.j.j.c.PracticeQuestionsCh6Test - Received password:
secretPassword. 2015-01-09 15:41:35.442 [Thread-5] [INFO ]
n.a.j.j.c.PracticeQuestionsCh6Test - Received password:
secretPassword. 2015-01-09 15:41:36.443 [Thread-7] [INFO ]
n.a.j.j.c.PracticeQuestionsCh6Test - Received password:
secretPassword. 2015-01-09 15:41:37.451 [Thread-9] [INFO ]
n.a.j.j.c.PracticeQuestionsCh6Test - Received password:
secretPassword.
我发现了错误。我创建的供应商每次都返回相同的值。以下是更正后的代码。不过有一个问题:为什么编译器会强制转换?
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;
}
我认为您正在走一条不必要的复杂道路。由于供应-测试-供应-测试-供应-测试序列是连续的,因此您只需要初始 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()
);
}
我正在练习 Cay S. Horstmann 的 "Java SE 8 for the Really Impatient" 一书中的练习。
Write a method
public static <T> CompletableFuture<T> repeat( Supplier<T> action, Predicate<T> until)
that asynchronously repeats the action until it produces a value that is accepted by the
until
function, which should also run asynchronously. Test with a function that reads ajava.net.PasswordAuthentication
from the console, and a function that simulates a validity check by sleeping for a second and then checking that the password is "secret".
我想出了以下代码,但随机密码生成策略似乎让我失望了。所有线程不断地选择相同的密码,这看起来很奇怪。
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 ] n.a.j.j.c.PracticeQuestionsCh6Test - Received password: secretPassword. 2015-01-09 15:41:34.371 [Thread-3] [INFO ] n.a.j.j.c.PracticeQuestionsCh6Test - Received password: secretPassword. 2015-01-09 15:41:35.442 [Thread-5] [INFO ] n.a.j.j.c.PracticeQuestionsCh6Test - Received password: secretPassword. 2015-01-09 15:41:36.443 [Thread-7] [INFO ] n.a.j.j.c.PracticeQuestionsCh6Test - Received password: secretPassword. 2015-01-09 15:41:37.451 [Thread-9] [INFO ] n.a.j.j.c.PracticeQuestionsCh6Test - Received password: secretPassword.
我发现了错误。我创建的供应商每次都返回相同的值。以下是更正后的代码。不过有一个问题:为什么编译器会强制转换?
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;
}
我认为您正在走一条不必要的复杂道路。由于供应-测试-供应-测试-供应-测试序列是连续的,因此您只需要初始 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()
);
}