CUBは子供の白熊

Java SE 8 実践プログラミングの練習問題を解く

第6章 並行処理の機能強化 : 問題 1 : AtomicReference

問題

多数のスレッドが更新する最大長の文字列を管理するプログラムを書け

解答

単語の一覧は、第2章で出てきた戦争と平和(war-and-peace.txt)を使う

■ 単語一覧の取得 … 単語数 = 575,343

String[] words;
String contents = new String(
    Files.readAllBytes(Paths.get("war-and-peace.txt")),
    StandardCharsets.UTF_8
);
words = contents.split("[\\P{L}]+");

ひとつのタスクで 5000 の単語を受け持ち、最大の長さの単語を見つける
最大の長さの単語はAtomicReference<String>で保持する
単語数は 575,343 個なので、タスク数は 116

Java 7 までのメソッドで実装

final int COUNT = 5000;
int nTask = (int)Math.ceil(words.length / (double)COUNT);

AtomicReference<String> longestWord = new AtomicReference<String>("");
ExecutorService pool = Executors.newCachedThreadPool();
for (int n = 0; n < nTask; n++) {
    int start = COUNT * n;
    int end   = Math.min(start + COUNT, words.length);
    pool.submit(
        () -> {
            for (int i = start; i < end; i++) {
                for (;;) {
                    String current = longestWord.get();
                    if (words[i].length() <= current.length())
                        break;
                    if (longestWord.compareAndSet(current, words[i]))
                        break;
                }
            }
        }
    );
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("最長の単語 : " + longestWord);

Java 8 で導入されたメソッドを使って実装

final int COUNT = 5000;
int nTask = (int)Math.ceil(words.length / (double)COUNT);

AtomicReference<String> longestWord = new AtomicReference<String>("");
ExecutorService pool = Executors.newCachedThreadPool();
for (int n = 0; n < nTask; n++) {
    int start = COUNT * n;
    int end   = Math.min(start + COUNT, words.length);
    pool.submit(
        () -> {
            for (int i = start; i < end; i++) {
                String word = words[i];
                longestWord.updateAndGet(
                    w -> word.length() > w.length() ? word: w
                );
            }
        }
    );
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("最長の単語 : " + longestWord);

longestWord.updateAndGetの箇所は、BinaryOperator<String>を使うaccumulateAndGetメソッドでもよい

longestWord.accumulateAndGet(
    words[i],
    (w0, w1) -> w1.length() > w0.length() ? w1: w0
);

でも、この問題ならupdateAndGetメソッドの方が自然です

ちなみに、最長の単語はcharacteristicallyです