Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Effective Java 輪読会 項目71-73

594 views

Published on

Published in: Technology
  • Login to see the comments

  • Be the first to like this

Effective Java 輪読会 項目71-73

  1. 1. Effective Java 輪読会 Item 71-73 開発部陳映融2014/3/5
  2. 2. 第10章並行性  項目66 共有された可変データへのアクセスを同期する  項目67 過剰な同期は避ける  項目68 スレッドよりエグゼキューターとタスクを選ぶ  項目69 wait とnotify よりコンカレンシーユーティリティを選ぶ  項目70 スレッド安全性を文書化する  項目71 遅延初期化を注意して使用する  項目72 スレッドスケジューラに依存しない  項目73 スレッドグループを避ける 2
  3. 3. Item 71 遅延初期化を注意して使用する
  4. 4. 遅延初期化について  遅延初期化とは? 4  フィールドの値が必要となるまで、フィールドの初期化を遅らせる行為  どこで使用する?  static フィールドとインスタンスフィールドの両方に適用可能  なんのための技法?  主に最適化のために使用  アクセスコストの増加を犠牲に  クラスの初期化コストやインスタンスの生成コストを減少させる  クラスとインスタンスの初期化において問題がある循環を断ち切るためにも  例: “Java Puzzlers”, Puzzle 51: What’s the Point?
  5. 5. 最適化での使用に対する最高の助言  「項目55 注意して最適化する」を思い出して...  必要でなければするな 5  フィールドがクラスの複数インスタンスの一部でだけアクセスされる  そしてフィールドの初期化にコストを要する場合は価値ある...かもしれない  実際のパフォーマンスを測定したうえ判断すべき  アクセスコストとクラス初期化コスト・インスタンス生成コストのトレードオフ  頻繁にアクセスされる場合は、かえってパフォーマンスを悪くする可能性も
  6. 6. 複数スレッドでの遅延初期化  複数スレッドがフィールドを共有する場合、同期の形式が重要で、そうしな 6 いと深刻なバグとなることも(項目66)  殆どの場合は、遅延初期化より普通の初期化が望ましい // インスタンスフィールドの初期化: final 修飾子使用した普通の初期化 private final FieldType field = computeFieldValue();  初期化循環を断ち切るために遅延初期化を使用した場合  同期されたアクセッサーを使用 // インスタンスフィールドの初期化: 同期されたアクセッサー内の遅延初期化 private FieldType field; synchronized FieldType getField() { if (field == null) field = computeFieldValue(); return field; }  上記の2つのイデオムはフィールド宣言とアクセッサー宣言にstatic 修飾子を 追加するだけでstatic フィールドに適用できる
  7. 7. 複数スレッドでの遅延初期化  パフォーマンスのためstatic フィールドに遅延初期化を適用する場合 7  遅延初期化(オンデマンド初期化)ホルダークラスイデオムを使用 // static フィールドに対する遅延初期化ホルダークラスイデオム private static class FieldHolder { static final FieldType field = computeFieldValue(); } static FieldType getField() { // 同期が不要なのでアクセスコストが実質的に増えない! return FieldHolder.field; // 呼び出された時に初めてFieldHolder.field 初期化 }  パフォーマンスのためインスタンスフィールドに遅延初期化を適用する場合  二重チェックイデオムを使用 // インスタンスフィールドに対する遅延初期化のための二重チェックイデオム private volatile FieldType field; FieldType getField() { FieldType result = field; // result がfield が初期化済みの場合は一回しか読み込まれないことを保証 if (result == null) { // 1回目検査(ロックなし) synchronized (this) { result = field; if (result == null) // 2回目検査(ロックあり) field = result = computeFieldValue(); } } return result; }
  8. 8. 複数スレッドでの遅延初期化  複数回の初期化を許容できるインスタンスフィールドの場合 8  二重チェックイデオムの変形:単一チェックイデオム // 単一チェックイデオム private volatile FieldType field; FieldType getField() { FieldType result = field; if (result == null) // 2回目のチェックがなくなり同期しなくなったため、複数回初期化されるかも field = result = computeFieldValue(); return result;  すべてのスレッドでフィールド値(long/double以外の基本型)が再計算 されても気にしない場合  二重チェックイデオムの変形:きわどい単一チェックイデオム  インスタンスフィールドのvolatile 修飾子を取り除く  long/double がダメな理由は、変数のwrite 処理はアトミックでないため[JLS17.7]  非標準技法であって普段用ではない  が、String のハッシュコードをキャッシュするため使用されている }
  9. 9. まとめ  殆どのフィールドは普通に初期化するべき(遅延なし)  遅延初期化を使用しなければならない場合は適切な技法を使用 9  初期化循環を断ち切るため  同期されたアクセッサー  パフォーマンス目標を達成するため:staticフィールド  遅延初期化ホルダークラスイデオム  パフォーマンス目標を達成するため:インスタンスフィールド  二重チェックイデオム(一回のみ初期化)  単一チェックイデオム(複数回初期化許容)
  10. 10. Item 72 スレッドスケジューラに依存しない
  11. 11. スレッドスケジューラとの付き合い方  スレッドスケジューラの仕事  実行可能な複数スレッドに対して、それぞれのスレッドの実行時間を決める  時間の決め方はOS やJVM 実装によって、ポリシーが異なる可能性がある ⇒ プログラムの正しさやパフォーマンスがスレッドスケジューラに依存すると移植 できなくなる  頑強で応答性のよい移植可能なプログラムを書くための最善策  実行可能なスレッドの数がプロセッサの数より、大きくなり過ぎないように保証 11 ⇒ 選択肢を狭めることで、スレッドスケジューラの動きの差異を抑える
  12. 12. 実行可能なスレッドの数を少なく保つ技法  個々のスレッドに何らか有益な処理を行わせてから、そこからさらなる処 理を待たせる  有益な処理を行っていない場合は動作すべきではない 12  ビジーウェイトは使用しないように ⇒ 他のスレッドの有益な処理の量を減らせてしまう  エグゼキューターフレームワークに関して言うと  スレッドプールを適切大きさにする  タスクを適度に小さくする  他のタスクから独立させる  タスクはあまり小さくするべきではない ⇒ ディスパッチするオーバーヘッドによりパフォーマンスが低下する
  13. 13. Thread.yield の使用について  Thread.yield メソッド  現在使用中のプロセッサを譲ってもいい、という意思表示  スレッドスケジューラへのヒントだけなので、無視されても文句言えない 13 ⇒ テスト可能なセマンティックスを持っていない  実行時間が得られなくて殆ど動かないプログラムに直面した場合  Thread.yield の呼び出しを入れてプログラムを「修理」する?  使用中のJVM 実装では動くかも知れないが  他のJVM 実装ではどうなるかわからない  アプリケーションを再構築して、並行して実行可能スレッド数を減らすべき  並行性検査のために使用しないこと  代わりにThread.sleep(1) を使用すること  すぐに戻ってくる可能性のあるThread.sleep(0) は使わないように
  14. 14. まとめ  アプリケーションの正しさに関してスレッドスケジューラに依存してはだめ  依存性のあるアプリケーションは頑健でもない、移植可能でもない  スケジューラに対するヒントとなる機構のThread.yield やスレッドの 優先順位に依存してもだめ  動作しているプログラムの品質を改善するのにスレッドの優先順位を控えめに 14 使用してもよい  殆ど動作しないプログラムの「修理」に使用するべきではない
  15. 15. Item 73 スレッドグループを避ける
  16. 16. スレッドグループって?  スレッドシステムが提供している基本的な抽象概念  スレッドの集合を表す  (以下省略)  セキュリティ目的のためにアプレットを隔離する仕組みとして考案された  役割を果たすことなかった  重要性も小さくなった(セキュリティモデルの標準書が言及しないほど) 16
  17. 17. スレッドグループが使用されない理由  たいして機能を提供していない  Thread の基本操作を一度に多くのスレッドに適用することを可能にする  いくつか推奨されない操作や、めったに使わない操作...  ThreadGroup API はスレッド安全性の観点から貧弱  enumerate:配列に入りきらないスレッドグループはそのまま無視される  activeCount:アクティブなスレッドの数の「推定値」を返す  activeGroupCount:アクティブなスレッドグループの数の「推定値」を返す  ThreadGroup API だけが提供している機能はもう存在しない  スレッドがスローした例外がキャッチされない場合の制御手段 17  リリース1.5 より前はThreadGroup.uncaughtException だけ  リリース1.5 以降はThread.setUncaughtExceptionHandler
  18. 18. まとめ  スレッドグループは有用な機能を提供していないし、提供している機能の 殆どに欠陥がある  スレッドグループを成功しなかった実験とみなして、その存在を無視しても よい  スレッドを論理的なグループを扱うクラスを設計するなら、おそらく、スレッ ドプールエグゼキューターを使用すべき  要するに、スレッドグループをなかったことにしよう 18

×