ひげぽんさんのエントリを読んで、図書館で貸し出し予約をして、実際に手元に届くまで半年。大人気です。
前半の第一部では、マルチスレッドプログラミングおける注意点やコツについて、後半の第二部では、Java の並行処理用の各種部品について、最後の16章では、Java のメモリモデルについてまで解説されている。
Java の本だけど、他言語メインの人も、第一部だけ読んで、(Javaに限らない) マルチスレッドプログラミングの肝を掴むとか、第二部を読んで、Java がマルチスレッドプログラミングのために工夫している部分について学ぶとかいった使い方ができると思う。
扱っている内容が実践的なので、ぜひとも手元に置いておきたい本なんだけど、絶版になってしまっているのが残念。
復刊ドットコムでの交渉がはじまっているみたいなので、そちらに期待。
Java並行処理プログラミング ―その「基盤」と「最新API」を究める―ソフトバンククリエイティブ 2006-11-22
売り上げランキング : 169096
おすすめ平均
Amazonで詳しく見る by G-Tools
以下、読書メモ。
1章 はじめに
- NPTL スレッドパッケージ (Linux2.6以降、カーネルのデフォルトのスレッド機能)
2章 スレッドセーフ
2-1 スレッドセーフって何?
- クラスは複数のスレッドからアクセスされたときに
正しく動作するならスレッドセーフである。
- ステートレスなオブジェクトはつねにスレッドセーフ
- 複合アクション
- リード・モディファイ・ライト
- チェック・ゼン・アクト
- 遅延初期化
- java.concurrent.atomic パッケージ
3章 オブジェクトを共有する
- メモリの可視性
3-1 可視性
- volatile を宣言されていない64ビットの数値 (double と long)
=> JVM は 64 ビットのリードとライトが
二つの32ビット操作である事を許している。
- 揮発性変数 (volatile):
そのリードはつねに、あるスレッドが書き込んだ最新の値を返す
- ロック => 可視性とアトミック性の両方を保証
揮発性変数 => 可視性だけを保証
3-2 公開と逸出
- NGなコード
- private なフィールドが外部から変更可能になる例
class UnsafeStates {
private String[] states = new String[] { "AK", "AL" };
public String[] getStates() { return this.states; }
}
- this参照の逸出
- スレッド拘束
- java.lang.ThreadLocal
- スタック拘束
3-4 不可変性
- Arrays.copyOf (Java6から)
3-5 安全な公開
- オブジェクトの利用と共有のためのポリシー
- スレッド拘束
- リードオンリーの共有
- スレッドセーフな共有
- ガード
4章 オブジェクトを組み立てる
4-1 スレッドセーフなクラスを設計する
- スレッドセーフなクラスの設計
- オブジェクトのステートを構成する変数 (ステート変数) を同定する
- ステート変数の値などを制約する不変更を同定する
- オブジェクトのステートへの並行アクセスを管理するためのポリシーを確立する
4-3 スレッドセーフを委譲する
- java.util.Collections.unmodifiableMap メソッド
- java.util.concurrent.CopyOnWriteArrayList
4-5 同期化ポリシーをドキュメントする
- ドキュメンテーションはスレッドセーフ性を管理するための
最高に強力なツールの一つ
5章 並行処理の構築部材
5-3 プロデューサ・コンシューマパターン
5-5 シンクロナイザ
シンクロナイザ: 自分のステートを使ってスレッドのコントロールフローを調停するオブジェクト
セマフォ、バリヤ、ラッチ など。
java.util.Conccurent
- CountDownLatch
- FutureTask (implements Future)
- Semaphore
- CyclicBarrier
- Exchanger
5-6
メモ化をスレッドセーフにする。
6章 タスクの実行
6-2 Executor フレームワーク
- java.util.conccurent
- Executor
- ExecutorService
- タスクの依頼と実行を分離する事で、
実行ポリシー (タスク実行の "what, whre, when, how") を簡単に指定できる。
- NG: java.util.Timer
OK: java.util.concurrent.ScheduledThreadPoolExecutor
6-3
- java.util.concurrent.CompletionService
- Future.get の時間指定付き呼び出し
- ExecutorService.invokeAll
7章 キャンセルとシャットダウン
Thread.stop と suspend には深刻な欠点があり、使うべきではない。
7-1. タスクのキャンセル
- volatile フィールドを使った協力的キャンセル
- スレッドは、インタラプションポリシーを持つべき
- インタラプションポリシーが分かっていないスレッドを
インタラプトしてはいけない
- java.util.concurrent.ThreadPoolExecutor の newTaskForフック
7-2. スレッドを使っているサービスを停止する
- スレッドを所有するサービスに、スレッドのシャットダウンの仕組みを持たせる
- プロデューサ・コンシューマ型のサービスのシャットダウン
- 毒薬 ("これをもらったら停止せよ" を意味するオブジェクト)
- java.lang.Thread.UncaughtExceptionHandler インタフェース
8章 スレッドプールを利用する
8-1 タスクと実行ポリシーの暗黙の結合
- Executor フレームワークではタスクの依頼と実行を切り離しきれない
タスクのタイプ
- 依存性のあるタスク
- スレッド拘束を利用しているタスク
- 応答時間に敏感なタスク
- ThreadLocal を使うタスク
8-3 ThreadPoolExecutor
- java.util.concurrent.ThreadPoolExecutor
- ThreadPoolExecutor のコンストラクタに渡すオプションのほとんどは、
コンストラクションの後でセッターメソッドを使って変更できる。
8-4 ThreadPoolExecutor を拡張する
- ThreadPoolExecutor は、拡張される前提で設計されている
9章 GUIアプリケーション
9-3
- SwingWorker クラス
長時間のタスクをバックグラウンドのスレッドで動かして、
GUIの応答性を維持するためのフレームワーク
10章 生存自己を防ぐ
10-1 デッドロック
- ロックの順序を固定する。
- 引数の順序で、ロック順が変化する場合は、
Sysytem.identityHashCode でロック順を誘導。
- 引き分けロック (第3のロック)
- ロックを保持した状態で、別のロックを保持するよそ者メソッドを呼び出すと、
デッドロックの可能性が高まる (しかも、分析が難しい)。
できるだけオープンコールを使うようにする。
オープンコール: ロックを保有しないでメソッドを呼び出す事
10-2 デッドロックの防止と診断
- Lock クラスの tryLock メソッド (時間制限付きのロック)
- スレッドダンプの出力
- Unix
- SIGQUITシグナル (kill -3)
- Ctrl-\
- Windows
- Ctrl-Break
10-3 そのほかの生存事故
- 飢餓状態
- シグナル喪失
- ライブロック
11章 実行性能とスケーラビリティ
11-2 アムダールの法則
ー 並列化できる部分と直列の部分の比率が重要。
11-3 スレッドがもたらす費用
- コンテキストスイッチ
- コンテキストスイッチの回数とそのために消費された時間の表示
- Unix : vmstat
- Windows: perfmon
- メモリの同期化
- ブロッキング
11-4 ロックの争奪を減らす
- 排他的ロックに代わる並行的フレンドリーな方法
- 並行コレクション
- リードライトロック
- 不可変オブジェクト
- アトミックオブジェクト
- etc.
- 通常は、オブジェクトを新たに作る方が、
既存のオブジェクトを同期化して使うよりもチープ
12章 平行プログラムを試験する
12-1 正しさを試験する
- スケジューリングのランダムさの確保
ループの中でスレッドをスタートさせると、スレッドは直列っぽく走る
(スレッドの作成とスタートは、程々に重い操作なので)
- 対応として、CountDownLatch や CyclicBarrier を使う
- 並列に動くスレッドの混じり具合をより多様にするために、
テストをマルチプロセッサのシステムで動かすべき。
さらに、アクティブなスレッドの数をCPUの数より多くする。
12-2 実行性能を試験する
- 複数のアルゴリズムを比較する
- 応答性を計測する
12-3 試験の落とし穴を避ける
- ガーベッジコレクション
- 動的コンパイル
- HotSpot は、-XX:+PrintCompilation で
動的コンパイル時にメッセージを出力する
- 処理系によるデッドコード (意味の無いコード) の排除
13章 明示的なロック
13-1 Lock と ReentrantLock
- java.util.concurrent.locks.ReentrantLock
- 固有のロックが持つ制約(*)を受けないロックの仕組み
* ロックの入手を待ってブロックしているスレッドにインタラプトできない
ロックを入手したコードブロックと同じブロックの中で解放する必要がある
- Lock.lockInterruptibly メソッドを使うと、
インタラプションへの応答性を維持したままロックの取得をトライできる
13-3 公平性 (fairness)
- 不公平なロックの良好な実行性能が、キューの公平性よりも重視される
(ロックの獲得にはコストがかかるので)
13-4 synchronized と ReentrantLock の使い分け
- ReentrantLock
- 時間制限付きのロック待機
- インタラプトできるロック待機
- コードブロックに縛られないロック
=> ReentrantLock は synchronized よりも明らかに危険なツール
13-5 リードライトロック
- ReadWriteLock インタフェース
14章 カスタムシンクロナイザを構築する
自作のシンクロナイザの作り方とその利用方法、メリットとデメリットについて。
固有の条件キュー
14-3 明示的な条件オブジェクト
- java.util.concurrent.locks.Condition インタフェース
14-5 AbstractQueuedSynchronizer
ロックとシンクロナイザを構築するためのフレームワーク
15章 アトミック変数とノンブロッキング同期化
15-2 ハードウェアの並行性サポート
- ほとんどの現代的なプロセッサに、
何らかの形のアトミックなリード・モディファイ・ライト命令がある
- compare and swap
- IA32 や Sparc など
15-3 アトミック変数
- ハードウェアの並行性サポートを直接使う
- 全12クラス
- 4つのグループ
- スカラー
- フィールドアップデータ
- 配列
- 複合変数
15-4 ノンブロッキングアルゴリズム
- 値を投機的に更新し、更新に失敗したらリトライする
# エラー忘却型コンピューティングに通じる考え方?
16章 Java のメモリモデル
- 半順序
- 排他性と可視性