第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());
さらに問題
各スレッドが単一のカウンタを更新するような実装にはしたくないはずである
何故か?
解答
インクリメントはアトミックな処理でないため、インクリメントのたびに排他制御をしなければならないから