CUBは子供の白熊

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

第2章 ストリーム API の使い方 : 問題 1 : ストリーム API を使わないで並列処理

問題 1

長い単語を数える処理を以下のようにforループで行った

List<String> words = 〜;
int count = 0;
for (String w : words) {
    if (w.length() > 12)
        count++;
}

これの並列バージョンを書け

解答

ストリーム API を使えば、ものすごく簡単に書けるのに…
まあ挑戦してみよう

List<String> wordsが与えられているとし、以下の変数を定義

final AtomicInteger totalCount = new AtomicInteger(0);

wordsをセグメントに分割して、個々のスレッドはそのセグメントをカウントしtotalCountに加算する

これらのスレッドを並列に実行し、各スレッドの処理が終わったときにtotalCountを見る

final int range = 1024;  // インデックスの範囲
final AtomicInteger totalCount = new AtomicInteger(0);
int nTask = (int)Math.ceil(words.size() / (double)range);

CountDownLatch latch = new CountDownLatch(nTask);
ExecutorService pool = Executors.newCachedThreadPool();
for (int n = 0; n < nTask; n++) {
    int start = range * n;
    int end   = Math.min(start + range, words.size());
    pool.submit(
        () -> {
            int count = 0;
            for (int i = start; i < end; i++) {
                if (words.get(i).length() > minLength)
                    count++;
            }
            totalCount.addAndGet(count);
            latch.countDown();
        }
    );
}
pool.shutdown();
latch.await();
System.out.println(minLength + "文字より長い単語の個数 : " + totalCount.get());

さらに問題

各スレッドが単一のカウンタを更新するような実装にはしたくないはずである
何故か?

解答

インクリメントはアトミックな処理でないため、インクリメントのたびに排他制御をしなければならないから