並列処理100本ノック
並列処理(java.util.concurrent)は、Java Gold試験においてStream APIと並ぶ最難関分野です。スレッドの安全性、ロック機構、そして高機能なコレクションの理解が求められます。
試験に出る「急所」を100個のチェックリストにまとめました。
1. スレッドの基本とRunnable/Callable (1-15)¶
Threadクラスの継承 vsRunnableインターフェースの実装Thread.start()とThread.run()の違い(後者は別スレッドにならない)Runnable:戻り値なし(void)、チェック例外を投げられないCallable<V>:戻り値あり(V)、チェック例外を投げられるThread.sleep():スレッドを一時停止(InterruptedException)Thread.join():対象スレッドの終了を待機Thread.interrupt():待機中のスレッドに割り込みisInterrupted()vsinterrupted()(後者はフラグをクリアする)- デーモンスレッド:メインスレッド終了時に強制終了されるスレッド
- スレッドの優先度(
setPriority)はOS依存で保証されない yield():他のスレッドに実行権を譲るヒント- スレッドの状態:
NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED synchronizedキーワードによる排他制御(インスタンスロック)synchronizedスタティックメソッド(クラスロック)-
volatileキーワード:メモリの可視性を保証し、キャッシュを無効化する2. Executor Service フレームワーク (16-30)¶
-
ExecutorService:スレッドの管理と実行を分離 Executors.newSingleThreadExecutor():単一スレッドExecutors.newFixedThreadPool(n):固定数のスレッドExecutors.newCachedThreadPool():必要に応じてスレッドを生成execute(Runnable):タスクの実行(戻り値なし)submit(Callable/Runnable):タスクの実行(Futureを返す)shutdown():新規タスクを拒否し、実行中のタスク完了を待つshutdownNow():実行中のタスクを中断し、待機中タスクを破棄isShutdown()vsisTerminated()awaitTermination():終了まで一定時間待機Future.get():結果が出るまでブロッキング(待機)Future.isDone():完了確認Future.cancel(boolean):タスクのキャンセルinvokeAll(Collection<Callable>):全タスクの完了を待つ-
invokeAny(Collection<Callable>):どれか1つ完了するのを待つ3. Scheduled Executor Service (31-40)¶
-
Executors.newScheduledThreadPool(n) schedule(Callable, delay, unit):一定時間後に実行scheduleAtFixedRate:**開始時点**を基準に一定間隔で実行scheduleWithFixedDelay:**終了時点**を基準に一定間隔で実行FixedRateでタスクが遅延した場合の挙動(連続して実行される)-
例外発生時の挙動(以降のスケジュールが停止する)
4. スレッドセーフなコレクション (41-55)¶
-
ConcurrentHashMap:セグメントロックによる高速なMap CopyOnWriteArrayList:書き込み時に配列をコピー(参照は高速)ConcurrentLinkedQueue:非ブロッキングQueueBlockingQueue:要素がない場合に待機するQueueArrayBlockingQueue:サイズ固定のBlockingQueueLinkedBlockingQueue:サイズ任意(デフォルト無限)PriorityBlockingQueue:優先度付きput()/take():空/満杯時にブロッキングするメソッドoffer(e, time, unit):タイムアウト付き追加ConcurrentSkipListMap:ソート済みスレッドセーフMapConcurrentSkipListSet:ソート済みスレッドセーフSet- 従来の
Collections.synchronizedListとの違い(ロック範囲の違い) ConcurrentHashMapはnullのキーと値を許可しない-
Iteratorのfail-safe挙動(実行中の変更でも例外が出ない)5. ロックと同期ユーティリティ (56-70)¶
-
ReentrantLock:明示的なロック(lock / unlock) lock.tryLock():ロック取得を試み、失敗しても即戻るlock.unlock()は必ずfinallyブロックで呼ぶReadWriteLock:読み取り同士はブロックしないロックReentrantReadWriteLockCyclicBarrier:全スレッドが揃うまで待機(再利用可能)CountDownLatch:カウントが0になるまで待機(再利用不可)Semaphore:利用可能なリソース数を制限acquire()/release()Exchanger:2つのスレッド間でデータを交換- デッドロック:互いにロックを待ち続けて停止
- ライブロック:互いに譲り合って進まない
- スレッド・スターベーション:特定の優先度が低いスレッドが実行されない
- アトミック変数(
AtomicInteger,AtomicReference等) -
CAS (Compare-And-Swap) アルゴリズムによる非ロック同期
6. Fork/Join フレームワーク (71-85)¶
-
ForkJoinPool:タスクを分割して並列実行 RecursiveTask<V>:戻り値があるタスクRecursiveAction:戻り値がないタスクcompute():タスク分割のロジックを書くメソッドfork():サブタスクを非同期実行join():サブタスクの結果を待機invoke():フォークとジョインを開始- ワークスティーリング:暇なスレッドが他のスレッドのタスクを奪って実行
- タスク分割の閾値(threshold)の設定重要性
-
commonPool():デフォルトのプール7. 並列Streamの深掘り (86-100)¶
-
parallelStream()とparallel()の関係 reduceの 3引数形式(identity, accumulator, combiner)- 並列時の
identityはaccumulatorの結果を変えない値であること combinerは 2つの部分結果をどう結合するか定義する- 無限ストリームを並列化する際のリスク
findAnyが並列で非決定的な値を返す理由unordered()による並列処理の最適化spliterator():並列分割のためのイテレータtrySplit():Spliteratorを分割するcharacteristics():ストリームの特性(ORDERED, DISTINCT等)- スレッドセーフでない状態へのアクセス(副作用)の危険性
System.out.printlnは同期処理なので並列Stream内ではボトルネックになるRuntime.getRuntime().availableProcessors():使用されるスレッド数の基準ThreadLocal:スレッドごとに独立した変数を保持CompletableFuture:非同期処理の連鎖(Java 8+)
試験対策アドバイス¶
Java Gold試験では、「どのインターフェースが戻り値を持ち、どの例外を投げるか」**や、「スレッドセーフなコレクションのプロパティ(nullの許容など)」**が非常に細かく問われます。
まずは最初のセクション、**「1. スレッドの基本とRunnable/Callable (1-15)」**の具体的なコード例から確認していきますか?