第6章 並行処理の機能強化 : 問題 11 : タスクを非同期に繰り返す
問題
次のメソッドを作成せよ
public static <T> CompletableFuture<T> repeat(Supplier<T> action, Predicate<T> until)
このメソッドはuntil
関数が受け入れる値を生成するまでaction
を非同期に繰り返す
そして、until
関数も非同期に実行する
解答
public static <T> CompletableFuture<T> repeat(Supplier<T> action, Predicate<T> until) { return CompletableFuture .supplyAsync(action) .thenComposeAsync( t -> { return until.test(t) ? CompletableFuture.completedFuture(t) : repeat(action, until); } ); }
再帰呼び出しを使うところが味噌
でも、こんなループ処理が遅延呼び出しで記述できるのは、すごい!
検証
以下の二つの関数
- コンソールから
PasswordAuthentication
を読み込む関数 - 1秒間スリープすることで正当性検査をシミュレートし、パスワードが "secret" であるかどうかを検査する関数
を用いてテストせよ
解答
Scanner stdin = new Scanner(System.in); CompletableFuture<PasswordAuthentication> f = repeat( // コンソールから PasswordAuthentication を読み込む () -> { System.out.print("User Name : "); String user = stdin.nextLine(); System.out.print("Password : "); String password = stdin.nextLine(); return new PasswordAuthentication(user, password.toCharArray()); }, // 正当性検査のシミュレーション auth -> { try { Thread.sleep(1000); } catch (InterruptedException ex) { // とりあえず無視 :-) } return new String(auth.getPassword()).equals("secret"); } ); f.thenAccept(auth -> { System.out.println(auth.getUserName()); }); // タスクの処理が終わる前に終了しないようにするおまじない ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.MINUTES); // ちょっと長めに System.out.println("終了");