SlideShare a Scribd company logo
1 of 39
Download to read offline
Java並列処理プログラミング	
  
第7章	
  キャンセルとシャットダウン	
2013/6/11	
  
遠山敏章
第7章 キャンセルとシャットダウン	
•  キャンセルとインタラプションの仕組みの紹介	
  
•  タスクやサービスをキャンセルリクエストに対
応付けるプログラムの書き方	
  
•  インタラプションとは	
  
– あるスレッドが別のスレッドに停止を求めることが
できる仕組み(Thread	
  interrupt())	
  
– Javaにはスレッドを途中で止める仕組みはない	
  
•  瞬時にスレッドを止めたいことはめったにない。	
  
– 後始末をしてから止まるべき	
  
目次	
•  7-­‐1	
  タスクのキャンセル	
  
–  7-­‐1-­‐1	
  インタラプション	
  
–  7-­‐1-­‐2	
  インタラプションポリシー	
  
–  7-­‐1-­‐3	
  インタラプションへの応答	
  
–  7-­‐1-­‐4	
  例:実行時間の制限	
  
–  7-­‐1-­‐5	
  Futureからキャンセルする	
  
–  7-­‐1-­‐6	
  インタラプトできないブロッキングの扱い方	
  
–  7-­‐1-­‐7	
  標準的でないキャンセルを newTaskFor	
  でカプセル化する	
  
•  7-­‐2	
  スレッドを使っているサービスを停止する	
  
–  7-­‐2-­‐1	
  例:ログ記録サービス	
  
–  7-­‐2-­‐2	
  ExecutorService	
  のシャットダウン	
  
–  7-­‐2-­‐3	
  毒薬	
  
–  7-­‐2-­‐4	
  例:1回かぎりの実行サービス	
  
–  7-­‐2-­‐5	
  shutdownNowの制約	
  
•  7-­‐3	
  スレッドの異常終了を扱う	
  
–  7-­‐3-­‐1	
  未捕捉例外ハンドラ	
  
•  7-­‐4	
  JVMのシャットダウン	
  
–  7-­‐4-­‐1	
  シャットダウンフック	
  
–  7-­‐4-­‐2	
  デーモンスレッド	
  
–  7-­‐4-­‐3	
  ファイナライザ	
  
7-­‐1	
  タスクのキャンセル	
  -­‐	
  1	
•  キャンセルする理由	
  
–  ユーザーがキャンセルをリクエストした	
  
•  GUIアプリのキャンセルボタンをクリック	
  
–  時間制限のある活動	
  
•  時間内に最良の結果を返すアプリ	
  
–  アプリケーションイベント	
  
•  複数のタスクがそれぞれ問題空間を探索しあるタスクが解
を見つけたら、他のタスクはキャンセル	
  
–  エラー	
  
•  クローラーのタスクのエラー	
  
–  シャットダウン	
  
•  穏やかなシャットダウン、緊急のシャットダウン
7-­‐1	
  タスクのキャンセル	
  -­‐	
  2	
•  スレッドを強制的に停止する安全な方法はな
い。	
  
•  協力的な仕組み	
  
1.  「キャンセルがリクエストされた」フラグの周期的
チェック(List	
  7-­‐1)	
  
1.  Cancelledをチェック。	
  
2.  Cancelled	
  をvolaNleにする	
  
7-­‐1	
  タスクのキャンセル	
  -­‐	
  3	
•  List	
  7-­‐2	
  素数生成クラス	
  
–  Sleepのインタラプトが発生してもfinallyで確実にキャンセル	
  
•  キャンセルされる側のキャンセルポリシー	
  
–  How,	
  when,	
  whatを定義	
  
•  How:	
  キャンセルをどうやって求めるか	
  
•  When:	
  キャンセルがリクエストされたことをいつチェックするか	
  
•  What:	
  キャンセルのリクエストに対してタスクはどんなアクションを実
行すべきか	
  
–  PrimeGeneratorのキャンセルポリシー	
  
•  How:	
  クライアントコードはcancelをコールしてキャンセルをリクエスト
する	
  
•  When:	
  PrimeGeneratorは素数が一つ見つかるたびにキャンセルを
チェックする	
  
•  What:	
  キャンセルがリクエストされたことを検出したら終了する	
  
7-­‐1-­‐1	
  インタラプション	
•  タスクがキャンセルフラグをチェックせず永久
に終わらないケース(List	
  7-­‐3)	
  
– BlockingQueueのputでブロックされたスレッドは
cancelledフラグをチェックできない	
  
•  ブロックするメソッドはインタラプションをサ
ポート(第5章)	
  
•  キャンセル以外の目的にインタラプションを使
うとプログラムの安定性を損ない、脆弱にな
る。
7-­‐1-­‐1	
  インタラプション -­‐	
  2	
•  Threadのインタラプション関連メソッド(List	
  7-­‐4)	
  
–  interrupt()	
  
•  目的のスレッドをインタラプト	
  
–  isInterrupted()	
  
•  目的のスレッドのインタラプテッドステータスを返す	
  
–  interrupted()	
  
•  現在のスレッド(このメソッドを呼んだスレッド)のインタラプテッドステータスをクリアし、その前の値を
返す。	
  
•  Thread	
  インタラプションの挙動	
  
–  スレッドをブロックしている時、インタラプテッドステータスをクリアし、InterruptedExcepNonを
投げる	
  
–  スレッドがブロックしていない時、インタラプテッドステータスがセットされ、それを調べるか、
調べないかはスレッドの自由。スティキーな状態。	
  
→ interruptメソッドは単に、インタラプションをリクエストしたというメッセージを伝えるだけ。「あなたのご
都合のよろしいとき(キャンセルポイント)にお仕事を中断してください」	
  
•  Interruptedがtrueなら何かをすべき。	
  
–  InterruptedExcepNonを投げる。Interruptを呼び出し、ステータスを復元
7-­‐1-­‐1	
  インタラプション -­‐	
  3	
•  InterruptedExcepNonを投げる。Interruptを呼
び出し、ステータスを復元	
  
•  BrokenPrimeProducerはフラグの代わりにイン
タラプションを使ってキャンセルをリクエスト
(List	
  7-­‐5)	
  
– 2つのインタラプションのチェック	
  
1.  ブロックするputメソッドの中	
  
2.  ループの明示的なポーリング	
  
→応答性を上げるために処理の前にインタラプションをチェック
するs	
  
7-­‐1-­‐2	
  インタラプションポリシー -­‐	
  1	
•  スレッドがインタラプションリクエストをどう解釈す
るかという取り決め	
  
•  例	
  
–  インタラプションが検出されたらいつ何をするか	
  
–  インタラプションに対してはどの仕事単位をアトミック
と見なすべきか	
  
–  どんなタイミングでインタラプションに応答すべきか	
  
•  スレッドはインタラプションポリシーを持つべき。	
  
•  妥当なインタラプションポリシーはスレッドレベル
又は、サービスレベルのキャンセル
7-­‐1-­‐2	
  インタラプションポリシー -­‐	
  2	
•  インタラプションへの反応はタスクとスレッドで違う	
  
•  1つのインタラプトリクエストの目的が複数あることもある	
  
–  スレッドプールのワーカースレッドにインタラプトする	
  
1.  現在のタスクをキャンセルせよ	
  
2.  このワーカースレッドをシャットダウンせよ	
  
–  タスクは自分が所有するスレッドの中では実行されない	
  
•  サービスからスレッドを借りる	
  
•  スレッドを所有しないコードはインタラプテッドステータスを保全して
スレッドのオーナーがインタラプションに対応できるように注意すべ
き。	
  
•  インタラプションリクエストの検出時は、実行中の仕事を完了してイ
ンタラプションに対応すれば良い	
  
•  単純にInterruptedExcepNonを呼び出し側に広めるのでないならば、
interruptedExcepNonをcatchしてからインタラプテッドステータスを
復元すべき。:Thread.currentThread().interrupt()	
  
7-­‐1-­‐3	
  インタラプションへの応答	
  -­‐	
  1	
•  InterruptedExcepNonの処理	
  
1.  例外を広める(List	
  7-­‐6)	
  
2.  インタラプテッドステータスを復元して呼び出し、
スタックの上のほうがそれを処理するようにする	
  
•  InterruptedEcepNonはもみ消してはいけない。
PrimeProducer(List	
  7-­‐3)でもみ消しているのはスレッ
ドが終了するだけのため。	
  
7-­‐1-­‐3	
  インタラプションへの応答	
  -­‐	
  2	
•  ブロックしてインタラプ書んを受け付けるメソッド
を呼び出す活動がキャンセルをサポートしてい
ない場合(List	
  7-­‐7)	
  
→ インタラプションのステータスを
InterruptedExcepNonのcatch後すぐに復元せず、ステー
タスを保存し、リターン直前に復元すべき	
  
–  	
   早くセットすると無限ループになることもあるため。	
  
•  コードがブロックしてインタラプションを受け付け
るメソッドを呼び出さない場合でも、インタラプ
テッドステータスをポーリングすることによって、
インタラプションへの応答性を持たせられます。	
  
7-­‐1-­‐4	
  例:実行時間の制限	
  -­‐	
  1	
•  無限の時間のかかる問題で「最大10分だけ答えを探
せ」と指定する。	
  
–  PrimeGenerator(List	
  7-­‐2)は1秒経つ前に
RunNmeExcepNonを投げたら気づかれないままになる。	
  
•  任意のRunnableを一定時間動かす試み(List	
  7-­‐8)	
  
–  ScheDuleExecutorServiceで一定時間後にキャンセル
(taskThread.interrupt())を呼び出して止める	
  
–  例外はNedRunを呼び出す側でcatchできる。	
  
–  この方法は反則。スレッドにインタラプションするためには
そのスレッドのインタラプションポリシーを知ってなければ、
ならないため。	
  
•  タイムアウトの前にタスクが終了したら、Returnした後で、スタート
する。
7-­‐1-­‐4	
  例:実行時間の制限	
  -­‐	
  2	
•  専用スレッドの中でタスクにインタラプトする例(List	
  
7-­‐9)	
  
–  2つの問題を解決	
  
•  aSecondPrimeOfPrimesの例外処理がcatchされなの問題	
  
•  List	
  7-­‐8のキャンセルタスクの実行タイミングの問題	
  
–  挙動	
  
•  NmedRunは時間制限付きのjoinをその新たに作られたスレッドで
実行。JoinでtaskThreadが終わるまで待つ	
  
•  例外が投げられていた場合はNmedRunの呼び出したスレッドの
中で再投する	
  
–  欠点	
  
•  制御が現在のスレッドに戻ったのはスレッドが正常終了したのか、
joinがタイムアウトしたのか分からない。(Thread	
  APIの欠点)	
  
7-­‐1-­‐5	
  Futureからキャンセルする	
•  Futureを使って、タスクをキャンセルする(List	
  
7-­‐10)	
  
– taskExec.submitでfutureを返す	
  
– task.cancel(false)で実行中ならインタラプトする	
  
– タスクがキャンセルより前に例外を投げたら再投
する	
  
7-­‐1-­‐6	
  インタラプトできないブロッキン
グの扱い -­‐	
  1	
•  ブロックするメソッドやブロックの仕組みのすべてがインタラプションに応答すると
はかぎらない。	
  
•  インタラプションに似た方法でストップさせることが可能な場合もありますが、その
ためにはスレッドがブロックしている理由に関する詳しい知識が必要。	
  
•  例	
  
–  Java.ioの同期ソケットI/O	
  
•  ソケットをクローズするとread/writeでブロックしているスレッドはSocketExcepNonを投げる。	
  
–  Java.nioの同期I/O	
  
•  InterrupNbleChannel上でウェイトしているスレッドにインタラプトするとClosedByInterrptExcepNonを投
げてチャネルをクローズする。	
  
–  Selectorによる非同期I/O	
  
•  スレッドがSelector.selectでブロックしていると、wakeupがClosedSelectorExcepNonを投げてselectを途
中でリターン。	
  
–  ロックの取得	
  
•  スレッドが固有のロック(2-­‐3-­‐1)を持ってブロックしている時は、そのスレッドをストップするためにできる
ことはない。	
  
•  ロックを取得させて処理を進行させ、ほかの方法でスレッドの注意を引くことしかできない。	
  
•  Lockを待ちながらインタラプションに応答できるLockクラスのlockInterrupNblyメソッド
7-­‐1-­‐6	
  インタラプトできないブロッキン
グの扱い	
  -­‐	
  2	
•  標準的でないキャンセルをカプセル化する
ReaderThread(List	
  7-­‐11)	
  
– Interruptをオーバーライドして、「標準のインタラ
プトを渡すこと」と「ソケットをクローズすること」の
両方をやらせる	
  
•  Readでブロックしていても、ブロックしてインタラプトを
受け付けるメソッドを呼び出し中でもスレッドを停止で
きる。
7-­‐1-­‐7	
  標準的でないキャンセルを
newTaskForでカプセル化する	
  -­‐	
  1	
•  標準的でないキャンセルをカプセル化したテ
クニックをThreadPoolExecutorのnewTaskFor
フックで洗練させる	
  
– ExecutorServiceにCallableを依頼するとき、submit
はタスクをキャンセルするために使えるFutureを
返します。	
  
– Future.cancel()をオーバーライドするとタスクに対
して「ソケットを使うスレッドのキャンセルをカプセ
ル化」(List	
  7-­‐11)するのと同等のことができる
7-­‐1-­‐7	
  標準的でないキャンセルを
newTaskForでカプセル化する	
  -­‐	
  2	
•  newTaskForでタスクの標準的でないキャンセルをカプセル化する(List	
  
7-­‐12)	
  
–  	
  CancellableTaskインタフェイス	
  
•  Callableをextendsしている	
  
•  CancellingExecutorがThreadPoolExecutorをextendsし、newTaskForをオーバーライトして
CancellableTaskに自分のFutureを作らせている。	
  
–  SocketUsingTaskアブストラクトクラス	
  
•  CancellableTaskをimplementsする	
  
•  Future.cancel()を定義してsuper.cancel()に加えソケットをクローズする。	
  
–  挙動	
  
•  SocketUsingTaskがFutureからキャンセルされると、ソケットがクローズされ、実行中のス
レッドがインタラプトされます。	
  
–  利点	
  
•  キャンセルに対するタスクの応答性が良くなる	
  
•  キャンセルにたいする応答性を維持できる	
  
•  ブロックしてインタラプトを受け付けるメソッドを安全に呼び出せる	
  
•  ブロックするソケットI/Oのメソッドも呼び出せる	
  
7-­‐2	
  スレッドを使っているサービスを停
止する	
•  シャットダウンするときはサービスが所有するスレッド
も終わる必要がある。	
  
•  カプセル化を壊さないためにはスレッドのオーナーで
はないコードがスレッドを操作してはいけない。(インタ
ラプト、プライオリティの変更)	
  
–  スレッドプールはそのワーカースレッドのオーナーなので、
スレッドがインタラプトされたら、スレッドプールが面倒を
見るべき。	
  
•  スレッドを所有するサービスはスレッドをシャットダウン
するライフサイクルメソッドを持つべき。	
  
–  ExecutorService	
  	
  
•  shutdon(),	
  shutdownNow()
7-­‐2-­‐1	
  例:ログ記録サービス	
  -­‐	
  1	
•  log	
  メソッドを呼び出してログメッセージをキューに入れ、そ
れを別のスレッドに処理させるクラス	
  
•  シャットダウンをサポートしないプロデューサー・コンシュー
マ型のログサービス(List	
  7-­‐13)	
  
–  BlockingQueueでログ記録スレッドにメッセージを渡す	
  
•  (マルチ)プロデューサー:ログの呼び出し	
  
•  (シングル)コンシューマ:ログの記録	
  
–  Takeを何度も呼んでログ記録スレッドを終わらせる	
  
•  Takeはインタラプションに応答する	
  
–  問題点	
  
•  ログに書かれていないメッセージの破棄	
  
•  logメソッドの中でブロックしていたスレッドがブロックを解かれない。	
  
–  プロデューサーとコンシューマの両方をキャンセルすることが
必要	
  
7-­‐2-­‐1	
  例:ログ記録サービス	
  -­‐	
  2	
•  「シャットダウンがリクエストされた」フラグで
メッセージの送付を禁止(List	
  7-­‐14)	
  
– 欠点	
  
•  競り合い状態があるので動作が不安定	
  
•  シャットダウンの後でもメッセージをキューに入れられ
ます。	
  
	
  →	
  log()でのブロッキングが発生	
  
7-­‐2-­‐1	
  例:ログ記録サービス	
  -­‐	
  3	
•  ログメッセージの送付をアトミックな操作にす
る(List	
  7-­‐15)	
  
– 競り合い状態をなくす	
  
– シャットダウンのチェックをアトミックにして、シャッ
トダウンでなければカウンターをインクリメントし、
メッセージを送付する権利を予約する	
  
7-­‐2-­‐2	
  ExecutorServiceのシャットダウン	
•  2つの終了方法は安全性と応答性のトレードオフを提
供	
  
1.  Shutdown():	
  穏やかなシャットダウン	
  
2.  shutdownNow():	
  唐突なシャットダウン	
  
•  shutdownNowが実行中のすべてのタスクのキャンセルを試みた
あと、まだスタートしていなかったタスクのリストを返す。	
  
•  自分のスレッドの管理をExecutorServiceに委譲	
  
–  ExecutorServiceを使うログサービス(List	
  7-­‐16)	
  
–  ExecutorServiceをカプセル化するとリンクがもう一つ増え
るので、所有のつながりがアプリケーションからサービス
へ、サービスからスレッドへと延びます。このつながりの
各メンバが、所有するサービスやスレッドのライフサイク
ルを管理する
7-­‐2-­‐3	
  毒薬(Poison	
  pill)	
•  キューに「これをもらったら停止せよ」を意味するオブジェクトを入れておく。	
  
–  コンシューマはシャットダウンの前に自分のキューの仕事を片づけることがで
きる	
  
–  プロデューサーはPoison	
  pillをキューに入れた後はタスクを追加してはいけな
い	
  
•  クローラのインデックスの挙動	
  
–  List	
  7-­‐17	
  IndexingServic:	
  POISON	
  Fileを定義	
  
–  List	
  7-­‐18	
  IndexServiceのプロデューサスレッド:	
  finally	
  でpoison	
  pillをput 	
  	
  
–  List	
  7-­‐19	
  IndexingServiceのコンシューマスレッド:	
  poison	
  pillなら	
  breakして終
了	
  
•  Poison	
  pill	
  はプロデューサー・コンシューマーの数が分かっているときだ
け使える	
  
–  各プロデューサーが一つPoison	
  pillをputし、コンシューマーはN個のPoison	
  
pillを確認した時に終了できる	
  
7-­‐2-­‐4	
  例:1回かぎりの実行サービス	
•  タスクのバッチ処理ですべてのタスクを処理
するまでリターンしないメソッド	
  
•  PrivateなExecutorをつかってライフサイクルを
簡単に管理できる。	
  
– 新着メールを複数のホストの上で並列にチェック
するcheckMailメソッド	
  (List	
  7-­‐20)	
  
•  Executorの寿命はメソッドの寿命とイコール
7-­‐2-­‐5	
  shutdownNowの制約	
  -­‐	
  1	
•  スタートしたけど、完了していないタスクを見つけ
る一般的な方法はない。	
  
–  2つの完了していない状態のタスク	
  
1.  スタートしなかったタスク	
  
2.  Executorがシャットダウンした時に進行中だったタスク	
  
•  シャットダウン時に進行中だったタスクを判断
するTrackingExecutor(List	
  7-­‐21)	
  
–  ExecutorServiceをカプセル化してexecuteを書き換え
る。	
  
–  シャットダウン後にキャンセルされたタスクを記録
7-­‐2-­‐5	
  shutdownNowの制約	
  -­‐	
  2	
•  WebCrawler	
  (List	
  7-­‐22):	
  TrackingExecutorのアプ
リ	
  
–  シャットダウン時にその状態を保存して、後でリス
タートすべき。	
  
–  クローラーがシャットダウンされると、下記のタスクの
urlを記録(stop	
  メソッド)	
  
•  スタートしなかったタスク	
  
•  途中でキャンセルされたタスク	
  
–  問題点	
  	
  
•  完了したタスクをキャンセルと記録する競り合い状態が発
せする。	
  
→冪等(idempotent,	
  2度実行しても一度実行した結果と同じ)にし
て対処
7-­‐3	
  スレッドの異常終了を扱う	
•  アプリケーションからスレッドが漏れることを検出
し、防ぐ方法	
  
•  スレッドが途中で死ぬ原因	
  
–  RunNmeExcepNon	
  
–  GUIアプリのイベントディスパッチスレッド(EDT)の喪失	
  
•  フリーズする	
  
•  スレッドプール内のワーカースレッドを構造化す
る例(List	
  7-­‐23)	
  
–  タスクが例外投げたらスレッドを殺す。	
  
–  不良なタスクがその後のタスクの実行を妨げないよう
にする
7-­‐3-­‐1	
  未捕捉例外ハンドラ -­‐	
  1	
•  UncaughtExcepNonHandler	
  
– Catchされていない例外でスレッドが死んだことを
検出(Thread	
  API)	
  
– 未捕捉の例外で終了したときJVMはイベントを
UncaughtExcepNonHandler(List	
  7-­‐24)に報告	
  
– UncaughtExcepNonHandlerがない時、スタックと
レースをSystem.errにプリント	
  
•  未捕捉の例外はアプリのQOS次第でログに
記録したりする。
7-­‐3-­‐1	
  未捕捉例外ハンドラ	
  -­‐	
  2	
•  プールのスレッドへのUncaughtExcepNonHandlerの設
定	
  
–  ThreadPoolExecutorのコンストラクタにThreadFactoryを渡
す。	
  
–  リカバリ処理をする時	
  
•  Runnable,	
  callableでタスクをラップ	
  
•  ThreadPoolExecutorのaderExecuteフックをオーバーライト	
  
•  タスクが投げた例外が未捕捉例外ハンドラまでいくの
はexecuteで依頼したタスクだけ。Submitで依頼したタ
スクでは、チェックされる例外もされない例外もすべて、
タスクのリターンステータスの一部とみなされる。	
  
•  Submitで依頼したタスクが例外で終了すると、
Future.getがExecuNonExcepNonでラップして再投する。	
  
7-­‐4	
  JVMのシャットダウン	
1.  整然(orderly)としたシャットダウン	
  
–  最後の「正規の」スレッドが終了	
  
–  System.exit	
  
–  プラットフォーム固有の終了(SIGINT,	
  Ctr-­‐C)	
  
2.  唐突(abrupt)なシャットダウン	
  
–  RunNme.halt	
  
–  OSからJVMのプロセスをkill(SIGKILL)
7-­‐4-­‐1	
  シャットダウンフック	
  -­‐	
  1	
•  整然としたシャットダウン	
  
–  Shutdown	
  hooksを全て実行する	
  
–  RunNme.addShutdownHookで登録したスレッド	
  
•  複数のシャットダウンフックをスタートする順序は不定	
  
•  シャットダウンフック完了 →	
  runFinalizersOnExitがtrueでファ
イナライザを実行	
  →	
  停止	
  
•  JVMの停止	
  
–  スレッドの停止、インタラプトもしないので、JVM停止
時に突然止まる	
  
–  唐突なシャットダウン。シャットダウンフックも実行され
ない。	
  
7-­‐4-­‐1	
  シャットダウンフック	
  -­‐	
  2	
•  シャットダウンフックはスレッドセーフであるべき。	
  
•  シャットダウンフックはサービスやアプリの後始末に使う	
  
–  一時ファイルの削除	
  
–  OSが自動に掃除してくれない資源を掃除	
  
•  LogServiceにシャットダウンフックを登録(List	
  7-­‐26)	
  
–  ログファイルを確実にクローズ	
  
–  シャットダウンフックは全員が平行に動く。	
  
•  アプリや他のシャットダウンフックがシャットダウンするかもしれない
サービスに依存しないようにする。	
  
•  全てのサービスに対応する一つのシャットダウンフックを使う。	
  
–  シャットダウンフックを使わない場合も逐次でシャットダウンアク
ションを呼び出すのは有効	
  
7-­‐4-­‐2	
  デーモンスレッド	
•  2種類のスレッド	
  
–  正規のスレッド	
  
–  デーモンスレッド	
  
•  メインのスレッド以外はデーモンスレッド	
  
•  2つのスレッドの違いは終了処理	
  
–  残っているスレッドがデーモンスレッドだけならJVMの
整然としたシャットダウン。デーモンスレッドはfinally
ブロックは実行されず破棄される。	
  
•  デーモンスレッドでは、サービスのライフサイク
ルを正しく管理できない。	
  
7-­‐4-­‐3	
  ファイナライザ	
•  ファイルやソケットのハンドルなど一部の資源
あh、要らなくなったらOSに明示的に返す必要
がある	
  
•  ファイナライザのあるクラスを使うことや書くこ
とを避ける。	
  
– 理由	
  
•  アクセスするステートの同期化が必要	
  
•  Finalizeメソッドを独自に定義しているオブジェクトの実
行性能の足を大きく引っ張る	
  
•  正しく書くのは大変難しい	
  
まとめ	
•  終末処理は、設計と実装の大きな難題の一
つ。	
  
•  強制的にキャンセルしたりスレッドを終わらせ
る仕組みがない。	
  
– 協力的な介入(インタラプト)の仕組みを使う	
  
•  FutureTaskとExecutorフレームワークを使うと、
キャンセルできるタスクやサービスを簡単に
構築できる。	
  
Q&A

More Related Content

What's hot

C# から java へのプログラム移植で体験したtddの効果は?
C# から java へのプログラム移植で体験したtddの効果は?C# から java へのプログラム移植で体験したtddの効果は?
C# から java へのプログラム移植で体験したtddの効果は?Shinichi Hirauchi
 
What's New in MySQL 5.7 Optimizer @MySQL User Conference Tokyo 2015
What's New in MySQL 5.7 Optimizer @MySQL User Conference Tokyo 2015What's New in MySQL 5.7 Optimizer @MySQL User Conference Tokyo 2015
What's New in MySQL 5.7 Optimizer @MySQL User Conference Tokyo 2015Mikiya Okuno
 
xUnit Test Patterns - Chapter11
xUnit Test Patterns - Chapter11xUnit Test Patterns - Chapter11
xUnit Test Patterns - Chapter11Takuto Wada
 
わかった気になるMySQL
わかった気になるMySQLわかった気になるMySQL
わかった気になるMySQLyoku0825
 
Performance Schema @ MySQL Casual #2
Performance Schema @ MySQL Casual #2Performance Schema @ MySQL Casual #2
Performance Schema @ MySQL Casual #2Mikiya Okuno
 
MySQL 5.6新機能解説@dbtechshowcase2012
MySQL 5.6新機能解説@dbtechshowcase2012MySQL 5.6新機能解説@dbtechshowcase2012
MySQL 5.6新機能解説@dbtechshowcase2012Mikiya Okuno
 
設計をする上で役にたった制約について
設計をする上で役にたった制約について設計をする上で役にたった制約について
設計をする上で役にたった制約についてIkki Takahashi
 
MySQLトラブル解析入門
MySQLトラブル解析入門MySQLトラブル解析入門
MySQLトラブル解析入門Mikiya Okuno
 
光のMySQL 5.7
光のMySQL 5.7光のMySQL 5.7
光のMySQL 5.7yoku0825
 
1.2新機能と1.2から始めるcql3
1.2新機能と1.2から始めるcql31.2新機能と1.2から始めるcql3
1.2新機能と1.2から始めるcql3seki_intheforest
 
RとSQLiteによるオミックス解析の促進
RとSQLiteによるオミックス解析の促進RとSQLiteによるオミックス解析の促進
RとSQLiteによるオミックス解析の促進弘毅 露崎
 
MySQL Cluster 7.4で楽しむスケールアウト @DB Tech Showcase 2015/06
MySQL Cluster 7.4で楽しむスケールアウト @DB Tech Showcase 2015/06MySQL Cluster 7.4で楽しむスケールアウト @DB Tech Showcase 2015/06
MySQL Cluster 7.4で楽しむスケールアウト @DB Tech Showcase 2015/06Mikiya Okuno
 
My sql casual_in_fukuoka_vol1
My sql casual_in_fukuoka_vol1My sql casual_in_fukuoka_vol1
My sql casual_in_fukuoka_vol1Makoto Haruyama
 
JavaScript The Good Parts Chapter 7
JavaScript The Good Parts Chapter 7JavaScript The Good Parts Chapter 7
JavaScript The Good Parts Chapter 7y torazuka
 
What's New in MySQL 5.7 Security
What's New in MySQL 5.7 SecurityWhat's New in MySQL 5.7 Security
What's New in MySQL 5.7 SecurityMikiya Okuno
 
MySQLerの7つ道具 plus
MySQLerの7つ道具 plusMySQLerの7つ道具 plus
MySQLerの7つ道具 plusyoku0825
 
RFC5717(Partial Lock Remote Procedure Call (RPC) for NETCONF)の勉強資料
RFC5717(Partial Lock Remote Procedure Call (RPC) for NETCONF)の勉強資料RFC5717(Partial Lock Remote Procedure Call (RPC) for NETCONF)の勉強資料
RFC5717(Partial Lock Remote Procedure Call (RPC) for NETCONF)の勉強資料Tetsuya Hasegawa
 
HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLHandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLakirahiguchi
 

What's hot (19)

C# から java へのプログラム移植で体験したtddの効果は?
C# から java へのプログラム移植で体験したtddの効果は?C# から java へのプログラム移植で体験したtddの効果は?
C# から java へのプログラム移植で体験したtddの効果は?
 
What's New in MySQL 5.7 Optimizer @MySQL User Conference Tokyo 2015
What's New in MySQL 5.7 Optimizer @MySQL User Conference Tokyo 2015What's New in MySQL 5.7 Optimizer @MySQL User Conference Tokyo 2015
What's New in MySQL 5.7 Optimizer @MySQL User Conference Tokyo 2015
 
xUnit Test Patterns - Chapter11
xUnit Test Patterns - Chapter11xUnit Test Patterns - Chapter11
xUnit Test Patterns - Chapter11
 
わかった気になるMySQL
わかった気になるMySQLわかった気になるMySQL
わかった気になるMySQL
 
Performance Schema @ MySQL Casual #2
Performance Schema @ MySQL Casual #2Performance Schema @ MySQL Casual #2
Performance Schema @ MySQL Casual #2
 
MySQL 5.6新機能解説@dbtechshowcase2012
MySQL 5.6新機能解説@dbtechshowcase2012MySQL 5.6新機能解説@dbtechshowcase2012
MySQL 5.6新機能解説@dbtechshowcase2012
 
設計をする上で役にたった制約について
設計をする上で役にたった制約について設計をする上で役にたった制約について
設計をする上で役にたった制約について
 
MySQLトラブル解析入門
MySQLトラブル解析入門MySQLトラブル解析入門
MySQLトラブル解析入門
 
光のMySQL 5.7
光のMySQL 5.7光のMySQL 5.7
光のMySQL 5.7
 
1.2新機能と1.2から始めるcql3
1.2新機能と1.2から始めるcql31.2新機能と1.2から始めるcql3
1.2新機能と1.2から始めるcql3
 
RとSQLiteによるオミックス解析の促進
RとSQLiteによるオミックス解析の促進RとSQLiteによるオミックス解析の促進
RとSQLiteによるオミックス解析の促進
 
MySQL Cluster 7.4で楽しむスケールアウト @DB Tech Showcase 2015/06
MySQL Cluster 7.4で楽しむスケールアウト @DB Tech Showcase 2015/06MySQL Cluster 7.4で楽しむスケールアウト @DB Tech Showcase 2015/06
MySQL Cluster 7.4で楽しむスケールアウト @DB Tech Showcase 2015/06
 
My sql casual_in_fukuoka_vol1
My sql casual_in_fukuoka_vol1My sql casual_in_fukuoka_vol1
My sql casual_in_fukuoka_vol1
 
JavaScript The Good Parts Chapter 7
JavaScript The Good Parts Chapter 7JavaScript The Good Parts Chapter 7
JavaScript The Good Parts Chapter 7
 
Effective Java 輪読会 項目74-75
Effective Java 輪読会 項目74-75Effective Java 輪読会 項目74-75
Effective Java 輪読会 項目74-75
 
What's New in MySQL 5.7 Security
What's New in MySQL 5.7 SecurityWhat's New in MySQL 5.7 Security
What's New in MySQL 5.7 Security
 
MySQLerの7つ道具 plus
MySQLerの7つ道具 plusMySQLerの7つ道具 plus
MySQLerの7つ道具 plus
 
RFC5717(Partial Lock Remote Procedure Call (RPC) for NETCONF)の勉強資料
RFC5717(Partial Lock Remote Procedure Call (RPC) for NETCONF)の勉強資料RFC5717(Partial Lock Remote Procedure Call (RPC) for NETCONF)の勉強資料
RFC5717(Partial Lock Remote Procedure Call (RPC) for NETCONF)の勉強資料
 
HandlerSocket plugin for MySQL
HandlerSocket plugin for MySQLHandlerSocket plugin for MySQL
HandlerSocket plugin for MySQL
 

Similar to 20130611 java concurrencyinpracticech7

Java concurrency in_practice_chap06
Java concurrency in_practice_chap06Java concurrency in_practice_chap06
Java concurrency in_practice_chap06ohtsuchi
 
【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門 【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門 sandai
 
Rubyにおけるトレース機構の刷新
Rubyにおけるトレース機構の刷新Rubyにおけるトレース機構の刷新
Rubyにおけるトレース機構の刷新Koichi Sasada
 
スレッドダンプの読み方
スレッドダンプの読み方スレッドダンプの読み方
スレッドダンプの読み方Funato Takashi
 
述語ロックの歴史 r2
述語ロックの歴史 r2述語ロックの歴史 r2
述語ロックの歴史 r2Sho Nakazono
 
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのかJavaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのかYoshitaka Kawashima
 
なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?ichirin2501
 
Project Loom - 限定継続と軽量スレッド -
Project Loom - 限定継続と軽量スレッド - Project Loom - 限定継続と軽量スレッド -
Project Loom - 限定継続と軽量スレッド - Yuichi Sakuraba
 
10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!bitter_fox
 
タダで始めるテストファースト入門 ~ C# Express + NUnit
タダで始めるテストファースト入門 ~ C# Express + NUnitタダで始めるテストファースト入門 ~ C# Express + NUnit
タダで始めるテストファースト入門 ~ C# Express + NUnitYasuhiko Yamamoto
 
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりませんレガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりませんTakahiro Okada
 

Similar to 20130611 java concurrencyinpracticech7 (12)

Java concurrency in_practice_chap06
Java concurrency in_practice_chap06Java concurrency in_practice_chap06
Java concurrency in_practice_chap06
 
【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門 【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門
 
Rubyにおけるトレース機構の刷新
Rubyにおけるトレース機構の刷新Rubyにおけるトレース機構の刷新
Rubyにおけるトレース機構の刷新
 
スレッドダンプの読み方
スレッドダンプの読み方スレッドダンプの読み方
スレッドダンプの読み方
 
Spock's world
Spock's worldSpock's world
Spock's world
 
述語ロックの歴史 r2
述語ロックの歴史 r2述語ロックの歴史 r2
述語ロックの歴史 r2
 
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのかJavaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
 
なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?なかったらINSERTしたいし、あるならロック取りたいやん?
なかったらINSERTしたいし、あるならロック取りたいやん?
 
Project Loom - 限定継続と軽量スレッド -
Project Loom - 限定継続と軽量スレッド - Project Loom - 限定継続と軽量スレッド -
Project Loom - 限定継続と軽量スレッド -
 
10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!
 
タダで始めるテストファースト入門 ~ C# Express + NUnit
タダで始めるテストファースト入門 ~ C# Express + NUnitタダで始めるテストファースト入門 ~ C# Express + NUnit
タダで始めるテストファースト入門 ~ C# Express + NUnit
 
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりませんレガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
 

20130611 java concurrencyinpracticech7

  • 2. 第7章 キャンセルとシャットダウン •  キャンセルとインタラプションの仕組みの紹介   •  タスクやサービスをキャンセルリクエストに対 応付けるプログラムの書き方   •  インタラプションとは   – あるスレッドが別のスレッドに停止を求めることが できる仕組み(Thread  interrupt())   – Javaにはスレッドを途中で止める仕組みはない   •  瞬時にスレッドを止めたいことはめったにない。   – 後始末をしてから止まるべき  
  • 3. 目次 •  7-­‐1  タスクのキャンセル   –  7-­‐1-­‐1  インタラプション   –  7-­‐1-­‐2  インタラプションポリシー   –  7-­‐1-­‐3  インタラプションへの応答   –  7-­‐1-­‐4  例:実行時間の制限   –  7-­‐1-­‐5  Futureからキャンセルする   –  7-­‐1-­‐6  インタラプトできないブロッキングの扱い方   –  7-­‐1-­‐7  標準的でないキャンセルを newTaskFor  でカプセル化する   •  7-­‐2  スレッドを使っているサービスを停止する   –  7-­‐2-­‐1  例:ログ記録サービス   –  7-­‐2-­‐2  ExecutorService  のシャットダウン   –  7-­‐2-­‐3  毒薬   –  7-­‐2-­‐4  例:1回かぎりの実行サービス   –  7-­‐2-­‐5  shutdownNowの制約   •  7-­‐3  スレッドの異常終了を扱う   –  7-­‐3-­‐1  未捕捉例外ハンドラ   •  7-­‐4  JVMのシャットダウン   –  7-­‐4-­‐1  シャットダウンフック   –  7-­‐4-­‐2  デーモンスレッド   –  7-­‐4-­‐3  ファイナライザ  
  • 4. 7-­‐1  タスクのキャンセル  -­‐  1 •  キャンセルする理由   –  ユーザーがキャンセルをリクエストした   •  GUIアプリのキャンセルボタンをクリック   –  時間制限のある活動   •  時間内に最良の結果を返すアプリ   –  アプリケーションイベント   •  複数のタスクがそれぞれ問題空間を探索しあるタスクが解 を見つけたら、他のタスクはキャンセル   –  エラー   •  クローラーのタスクのエラー   –  シャットダウン   •  穏やかなシャットダウン、緊急のシャットダウン
  • 5. 7-­‐1  タスクのキャンセル  -­‐  2 •  スレッドを強制的に停止する安全な方法はな い。   •  協力的な仕組み   1.  「キャンセルがリクエストされた」フラグの周期的 チェック(List  7-­‐1)   1.  Cancelledをチェック。   2.  Cancelled  をvolaNleにする  
  • 6. 7-­‐1  タスクのキャンセル  -­‐  3 •  List  7-­‐2  素数生成クラス   –  Sleepのインタラプトが発生してもfinallyで確実にキャンセル   •  キャンセルされる側のキャンセルポリシー   –  How,  when,  whatを定義   •  How:  キャンセルをどうやって求めるか   •  When:  キャンセルがリクエストされたことをいつチェックするか   •  What:  キャンセルのリクエストに対してタスクはどんなアクションを実 行すべきか   –  PrimeGeneratorのキャンセルポリシー   •  How:  クライアントコードはcancelをコールしてキャンセルをリクエスト する   •  When:  PrimeGeneratorは素数が一つ見つかるたびにキャンセルを チェックする   •  What:  キャンセルがリクエストされたことを検出したら終了する  
  • 7. 7-­‐1-­‐1  インタラプション •  タスクがキャンセルフラグをチェックせず永久 に終わらないケース(List  7-­‐3)   – BlockingQueueのputでブロックされたスレッドは cancelledフラグをチェックできない   •  ブロックするメソッドはインタラプションをサ ポート(第5章)   •  キャンセル以外の目的にインタラプションを使 うとプログラムの安定性を損ない、脆弱にな る。
  • 8. 7-­‐1-­‐1  インタラプション -­‐  2 •  Threadのインタラプション関連メソッド(List  7-­‐4)   –  interrupt()   •  目的のスレッドをインタラプト   –  isInterrupted()   •  目的のスレッドのインタラプテッドステータスを返す   –  interrupted()   •  現在のスレッド(このメソッドを呼んだスレッド)のインタラプテッドステータスをクリアし、その前の値を 返す。   •  Thread  インタラプションの挙動   –  スレッドをブロックしている時、インタラプテッドステータスをクリアし、InterruptedExcepNonを 投げる   –  スレッドがブロックしていない時、インタラプテッドステータスがセットされ、それを調べるか、 調べないかはスレッドの自由。スティキーな状態。   → interruptメソッドは単に、インタラプションをリクエストしたというメッセージを伝えるだけ。「あなたのご 都合のよろしいとき(キャンセルポイント)にお仕事を中断してください」   •  Interruptedがtrueなら何かをすべき。   –  InterruptedExcepNonを投げる。Interruptを呼び出し、ステータスを復元
  • 9. 7-­‐1-­‐1  インタラプション -­‐  3 •  InterruptedExcepNonを投げる。Interruptを呼 び出し、ステータスを復元   •  BrokenPrimeProducerはフラグの代わりにイン タラプションを使ってキャンセルをリクエスト (List  7-­‐5)   – 2つのインタラプションのチェック   1.  ブロックするputメソッドの中   2.  ループの明示的なポーリング   →応答性を上げるために処理の前にインタラプションをチェック するs  
  • 10. 7-­‐1-­‐2  インタラプションポリシー -­‐  1 •  スレッドがインタラプションリクエストをどう解釈す るかという取り決め   •  例   –  インタラプションが検出されたらいつ何をするか   –  インタラプションに対してはどの仕事単位をアトミック と見なすべきか   –  どんなタイミングでインタラプションに応答すべきか   •  スレッドはインタラプションポリシーを持つべき。   •  妥当なインタラプションポリシーはスレッドレベル 又は、サービスレベルのキャンセル
  • 11. 7-­‐1-­‐2  インタラプションポリシー -­‐  2 •  インタラプションへの反応はタスクとスレッドで違う   •  1つのインタラプトリクエストの目的が複数あることもある   –  スレッドプールのワーカースレッドにインタラプトする   1.  現在のタスクをキャンセルせよ   2.  このワーカースレッドをシャットダウンせよ   –  タスクは自分が所有するスレッドの中では実行されない   •  サービスからスレッドを借りる   •  スレッドを所有しないコードはインタラプテッドステータスを保全して スレッドのオーナーがインタラプションに対応できるように注意すべ き。   •  インタラプションリクエストの検出時は、実行中の仕事を完了してイ ンタラプションに対応すれば良い   •  単純にInterruptedExcepNonを呼び出し側に広めるのでないならば、 interruptedExcepNonをcatchしてからインタラプテッドステータスを 復元すべき。:Thread.currentThread().interrupt()  
  • 12. 7-­‐1-­‐3  インタラプションへの応答  -­‐  1 •  InterruptedExcepNonの処理   1.  例外を広める(List  7-­‐6)   2.  インタラプテッドステータスを復元して呼び出し、 スタックの上のほうがそれを処理するようにする   •  InterruptedEcepNonはもみ消してはいけない。 PrimeProducer(List  7-­‐3)でもみ消しているのはスレッ ドが終了するだけのため。  
  • 13. 7-­‐1-­‐3  インタラプションへの応答  -­‐  2 •  ブロックしてインタラプ書んを受け付けるメソッド を呼び出す活動がキャンセルをサポートしてい ない場合(List  7-­‐7)   → インタラプションのステータスを InterruptedExcepNonのcatch後すぐに復元せず、ステー タスを保存し、リターン直前に復元すべき   –    早くセットすると無限ループになることもあるため。   •  コードがブロックしてインタラプションを受け付け るメソッドを呼び出さない場合でも、インタラプ テッドステータスをポーリングすることによって、 インタラプションへの応答性を持たせられます。  
  • 14. 7-­‐1-­‐4  例:実行時間の制限  -­‐  1 •  無限の時間のかかる問題で「最大10分だけ答えを探 せ」と指定する。   –  PrimeGenerator(List  7-­‐2)は1秒経つ前に RunNmeExcepNonを投げたら気づかれないままになる。   •  任意のRunnableを一定時間動かす試み(List  7-­‐8)   –  ScheDuleExecutorServiceで一定時間後にキャンセル (taskThread.interrupt())を呼び出して止める   –  例外はNedRunを呼び出す側でcatchできる。   –  この方法は反則。スレッドにインタラプションするためには そのスレッドのインタラプションポリシーを知ってなければ、 ならないため。   •  タイムアウトの前にタスクが終了したら、Returnした後で、スタート する。
  • 15. 7-­‐1-­‐4  例:実行時間の制限  -­‐  2 •  専用スレッドの中でタスクにインタラプトする例(List   7-­‐9)   –  2つの問題を解決   •  aSecondPrimeOfPrimesの例外処理がcatchされなの問題   •  List  7-­‐8のキャンセルタスクの実行タイミングの問題   –  挙動   •  NmedRunは時間制限付きのjoinをその新たに作られたスレッドで 実行。JoinでtaskThreadが終わるまで待つ   •  例外が投げられていた場合はNmedRunの呼び出したスレッドの 中で再投する   –  欠点   •  制御が現在のスレッドに戻ったのはスレッドが正常終了したのか、 joinがタイムアウトしたのか分からない。(Thread  APIの欠点)  
  • 16. 7-­‐1-­‐5  Futureからキャンセルする •  Futureを使って、タスクをキャンセルする(List   7-­‐10)   – taskExec.submitでfutureを返す   – task.cancel(false)で実行中ならインタラプトする   – タスクがキャンセルより前に例外を投げたら再投 する  
  • 17. 7-­‐1-­‐6  インタラプトできないブロッキン グの扱い -­‐  1 •  ブロックするメソッドやブロックの仕組みのすべてがインタラプションに応答すると はかぎらない。   •  インタラプションに似た方法でストップさせることが可能な場合もありますが、その ためにはスレッドがブロックしている理由に関する詳しい知識が必要。   •  例   –  Java.ioの同期ソケットI/O   •  ソケットをクローズするとread/writeでブロックしているスレッドはSocketExcepNonを投げる。   –  Java.nioの同期I/O   •  InterrupNbleChannel上でウェイトしているスレッドにインタラプトするとClosedByInterrptExcepNonを投 げてチャネルをクローズする。   –  Selectorによる非同期I/O   •  スレッドがSelector.selectでブロックしていると、wakeupがClosedSelectorExcepNonを投げてselectを途 中でリターン。   –  ロックの取得   •  スレッドが固有のロック(2-­‐3-­‐1)を持ってブロックしている時は、そのスレッドをストップするためにできる ことはない。   •  ロックを取得させて処理を進行させ、ほかの方法でスレッドの注意を引くことしかできない。   •  Lockを待ちながらインタラプションに応答できるLockクラスのlockInterrupNblyメソッド
  • 18. 7-­‐1-­‐6  インタラプトできないブロッキン グの扱い  -­‐  2 •  標準的でないキャンセルをカプセル化する ReaderThread(List  7-­‐11)   – Interruptをオーバーライドして、「標準のインタラ プトを渡すこと」と「ソケットをクローズすること」の 両方をやらせる   •  Readでブロックしていても、ブロックしてインタラプトを 受け付けるメソッドを呼び出し中でもスレッドを停止で きる。
  • 19. 7-­‐1-­‐7  標準的でないキャンセルを newTaskForでカプセル化する  -­‐  1 •  標準的でないキャンセルをカプセル化したテ クニックをThreadPoolExecutorのnewTaskFor フックで洗練させる   – ExecutorServiceにCallableを依頼するとき、submit はタスクをキャンセルするために使えるFutureを 返します。   – Future.cancel()をオーバーライドするとタスクに対 して「ソケットを使うスレッドのキャンセルをカプセ ル化」(List  7-­‐11)するのと同等のことができる
  • 20. 7-­‐1-­‐7  標準的でないキャンセルを newTaskForでカプセル化する  -­‐  2 •  newTaskForでタスクの標準的でないキャンセルをカプセル化する(List   7-­‐12)   –   CancellableTaskインタフェイス   •  Callableをextendsしている   •  CancellingExecutorがThreadPoolExecutorをextendsし、newTaskForをオーバーライトして CancellableTaskに自分のFutureを作らせている。   –  SocketUsingTaskアブストラクトクラス   •  CancellableTaskをimplementsする   •  Future.cancel()を定義してsuper.cancel()に加えソケットをクローズする。   –  挙動   •  SocketUsingTaskがFutureからキャンセルされると、ソケットがクローズされ、実行中のス レッドがインタラプトされます。   –  利点   •  キャンセルに対するタスクの応答性が良くなる   •  キャンセルにたいする応答性を維持できる   •  ブロックしてインタラプトを受け付けるメソッドを安全に呼び出せる   •  ブロックするソケットI/Oのメソッドも呼び出せる  
  • 21. 7-­‐2  スレッドを使っているサービスを停 止する •  シャットダウンするときはサービスが所有するスレッド も終わる必要がある。   •  カプセル化を壊さないためにはスレッドのオーナーで はないコードがスレッドを操作してはいけない。(インタ ラプト、プライオリティの変更)   –  スレッドプールはそのワーカースレッドのオーナーなので、 スレッドがインタラプトされたら、スレッドプールが面倒を 見るべき。   •  スレッドを所有するサービスはスレッドをシャットダウン するライフサイクルメソッドを持つべき。   –  ExecutorService     •  shutdon(),  shutdownNow()
  • 22. 7-­‐2-­‐1  例:ログ記録サービス  -­‐  1 •  log  メソッドを呼び出してログメッセージをキューに入れ、そ れを別のスレッドに処理させるクラス   •  シャットダウンをサポートしないプロデューサー・コンシュー マ型のログサービス(List  7-­‐13)   –  BlockingQueueでログ記録スレッドにメッセージを渡す   •  (マルチ)プロデューサー:ログの呼び出し   •  (シングル)コンシューマ:ログの記録   –  Takeを何度も呼んでログ記録スレッドを終わらせる   •  Takeはインタラプションに応答する   –  問題点   •  ログに書かれていないメッセージの破棄   •  logメソッドの中でブロックしていたスレッドがブロックを解かれない。   –  プロデューサーとコンシューマの両方をキャンセルすることが 必要  
  • 23. 7-­‐2-­‐1  例:ログ記録サービス  -­‐  2 •  「シャットダウンがリクエストされた」フラグで メッセージの送付を禁止(List  7-­‐14)   – 欠点   •  競り合い状態があるので動作が不安定   •  シャットダウンの後でもメッセージをキューに入れられ ます。    →  log()でのブロッキングが発生  
  • 24. 7-­‐2-­‐1  例:ログ記録サービス  -­‐  3 •  ログメッセージの送付をアトミックな操作にす る(List  7-­‐15)   – 競り合い状態をなくす   – シャットダウンのチェックをアトミックにして、シャッ トダウンでなければカウンターをインクリメントし、 メッセージを送付する権利を予約する  
  • 25. 7-­‐2-­‐2  ExecutorServiceのシャットダウン •  2つの終了方法は安全性と応答性のトレードオフを提 供   1.  Shutdown():  穏やかなシャットダウン   2.  shutdownNow():  唐突なシャットダウン   •  shutdownNowが実行中のすべてのタスクのキャンセルを試みた あと、まだスタートしていなかったタスクのリストを返す。   •  自分のスレッドの管理をExecutorServiceに委譲   –  ExecutorServiceを使うログサービス(List  7-­‐16)   –  ExecutorServiceをカプセル化するとリンクがもう一つ増え るので、所有のつながりがアプリケーションからサービス へ、サービスからスレッドへと延びます。このつながりの 各メンバが、所有するサービスやスレッドのライフサイク ルを管理する
  • 26. 7-­‐2-­‐3  毒薬(Poison  pill) •  キューに「これをもらったら停止せよ」を意味するオブジェクトを入れておく。   –  コンシューマはシャットダウンの前に自分のキューの仕事を片づけることがで きる   –  プロデューサーはPoison  pillをキューに入れた後はタスクを追加してはいけな い   •  クローラのインデックスの挙動   –  List  7-­‐17  IndexingServic:  POISON  Fileを定義   –  List  7-­‐18  IndexServiceのプロデューサスレッド:  finally  でpoison  pillをput     –  List  7-­‐19  IndexingServiceのコンシューマスレッド:  poison  pillなら  breakして終 了   •  Poison  pill  はプロデューサー・コンシューマーの数が分かっているときだ け使える   –  各プロデューサーが一つPoison  pillをputし、コンシューマーはN個のPoison   pillを確認した時に終了できる  
  • 27. 7-­‐2-­‐4  例:1回かぎりの実行サービス •  タスクのバッチ処理ですべてのタスクを処理 するまでリターンしないメソッド   •  PrivateなExecutorをつかってライフサイクルを 簡単に管理できる。   – 新着メールを複数のホストの上で並列にチェック するcheckMailメソッド  (List  7-­‐20)   •  Executorの寿命はメソッドの寿命とイコール
  • 28. 7-­‐2-­‐5  shutdownNowの制約  -­‐  1 •  スタートしたけど、完了していないタスクを見つけ る一般的な方法はない。   –  2つの完了していない状態のタスク   1.  スタートしなかったタスク   2.  Executorがシャットダウンした時に進行中だったタスク   •  シャットダウン時に進行中だったタスクを判断 するTrackingExecutor(List  7-­‐21)   –  ExecutorServiceをカプセル化してexecuteを書き換え る。   –  シャットダウン後にキャンセルされたタスクを記録
  • 29. 7-­‐2-­‐5  shutdownNowの制約  -­‐  2 •  WebCrawler  (List  7-­‐22):  TrackingExecutorのアプ リ   –  シャットダウン時にその状態を保存して、後でリス タートすべき。   –  クローラーがシャットダウンされると、下記のタスクの urlを記録(stop  メソッド)   •  スタートしなかったタスク   •  途中でキャンセルされたタスク   –  問題点     •  完了したタスクをキャンセルと記録する競り合い状態が発 せする。   →冪等(idempotent,  2度実行しても一度実行した結果と同じ)にし て対処
  • 30. 7-­‐3  スレッドの異常終了を扱う •  アプリケーションからスレッドが漏れることを検出 し、防ぐ方法   •  スレッドが途中で死ぬ原因   –  RunNmeExcepNon   –  GUIアプリのイベントディスパッチスレッド(EDT)の喪失   •  フリーズする   •  スレッドプール内のワーカースレッドを構造化す る例(List  7-­‐23)   –  タスクが例外投げたらスレッドを殺す。   –  不良なタスクがその後のタスクの実行を妨げないよう にする
  • 31. 7-­‐3-­‐1  未捕捉例外ハンドラ -­‐  1 •  UncaughtExcepNonHandler   – Catchされていない例外でスレッドが死んだことを 検出(Thread  API)   – 未捕捉の例外で終了したときJVMはイベントを UncaughtExcepNonHandler(List  7-­‐24)に報告   – UncaughtExcepNonHandlerがない時、スタックと レースをSystem.errにプリント   •  未捕捉の例外はアプリのQOS次第でログに 記録したりする。
  • 32. 7-­‐3-­‐1  未捕捉例外ハンドラ  -­‐  2 •  プールのスレッドへのUncaughtExcepNonHandlerの設 定   –  ThreadPoolExecutorのコンストラクタにThreadFactoryを渡 す。   –  リカバリ処理をする時   •  Runnable,  callableでタスクをラップ   •  ThreadPoolExecutorのaderExecuteフックをオーバーライト   •  タスクが投げた例外が未捕捉例外ハンドラまでいくの はexecuteで依頼したタスクだけ。Submitで依頼したタ スクでは、チェックされる例外もされない例外もすべて、 タスクのリターンステータスの一部とみなされる。   •  Submitで依頼したタスクが例外で終了すると、 Future.getがExecuNonExcepNonでラップして再投する。  
  • 33. 7-­‐4  JVMのシャットダウン 1.  整然(orderly)としたシャットダウン   –  最後の「正規の」スレッドが終了   –  System.exit   –  プラットフォーム固有の終了(SIGINT,  Ctr-­‐C)   2.  唐突(abrupt)なシャットダウン   –  RunNme.halt   –  OSからJVMのプロセスをkill(SIGKILL)
  • 34. 7-­‐4-­‐1  シャットダウンフック  -­‐  1 •  整然としたシャットダウン   –  Shutdown  hooksを全て実行する   –  RunNme.addShutdownHookで登録したスレッド   •  複数のシャットダウンフックをスタートする順序は不定   •  シャットダウンフック完了 →  runFinalizersOnExitがtrueでファ イナライザを実行  →  停止   •  JVMの停止   –  スレッドの停止、インタラプトもしないので、JVM停止 時に突然止まる   –  唐突なシャットダウン。シャットダウンフックも実行され ない。  
  • 35. 7-­‐4-­‐1  シャットダウンフック  -­‐  2 •  シャットダウンフックはスレッドセーフであるべき。   •  シャットダウンフックはサービスやアプリの後始末に使う   –  一時ファイルの削除   –  OSが自動に掃除してくれない資源を掃除   •  LogServiceにシャットダウンフックを登録(List  7-­‐26)   –  ログファイルを確実にクローズ   –  シャットダウンフックは全員が平行に動く。   •  アプリや他のシャットダウンフックがシャットダウンするかもしれない サービスに依存しないようにする。   •  全てのサービスに対応する一つのシャットダウンフックを使う。   –  シャットダウンフックを使わない場合も逐次でシャットダウンアク ションを呼び出すのは有効  
  • 36. 7-­‐4-­‐2  デーモンスレッド •  2種類のスレッド   –  正規のスレッド   –  デーモンスレッド   •  メインのスレッド以外はデーモンスレッド   •  2つのスレッドの違いは終了処理   –  残っているスレッドがデーモンスレッドだけならJVMの 整然としたシャットダウン。デーモンスレッドはfinally ブロックは実行されず破棄される。   •  デーモンスレッドでは、サービスのライフサイク ルを正しく管理できない。  
  • 37. 7-­‐4-­‐3  ファイナライザ •  ファイルやソケットのハンドルなど一部の資源 あh、要らなくなったらOSに明示的に返す必要 がある   •  ファイナライザのあるクラスを使うことや書くこ とを避ける。   – 理由   •  アクセスするステートの同期化が必要   •  Finalizeメソッドを独自に定義しているオブジェクトの実 行性能の足を大きく引っ張る   •  正しく書くのは大変難しい  
  • 38. まとめ •  終末処理は、設計と実装の大きな難題の一 つ。   •  強制的にキャンセルしたりスレッドを終わらせ る仕組みがない。   – 協力的な介入(インタラプト)の仕組みを使う   •  FutureTaskとExecutorフレームワークを使うと、 キャンセルできるタスクやサービスを簡単に 構築できる。  
  • 39. Q&A