第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
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);
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
です