Java

シャットダウンフック API の設計

ドキュメントの目次
次に示す質問と回答に、シャットダウンフック API の設計上の問題点を示します。

シャットダウン時の処理には、 runFinalizersOnExit は使用できないのですか。

Runtime.runFinalizersOnExit メソッド、または System クラス内の同等なメソッドを使用して、終了によって VM がシャットダウンしたときにアクションが実行されるようにスケジュールすることができます。ただし、この方法は、強制終了によるシャットダウンが起動された場合にはうまく機能しません。また、本質的に安全ではないため、JavaTM?2 プラットフォームのバージョン 1.2 では推奨されていません

なぜ VM のシャットダウンの原因についての情報を通知しないのですか。

一部のプラットフォームのネイティブプロセスでは、終了によるシャットダウンと強制終了によるシャットダウンは区別されません。また、豊富な機能を提供しているプラットフォームもあります。 たとえば、システムの一時停止や再起動、または電源障害が発生する可能性について通知する機能が提供されているプラットフォームがあります。つまり、これらの情報を移植可能な方法で一般化することはできません。

VM がクラッシュした場合に、シャットダウンフックは実行されますか。

ネイティブコードのエラーが原因で VM がクラッシュした場合は、フックの実行は保証されません。

なぜシャットダウンフックは並列に実行されるのですか。登録の逆方向に実行する方が適切ではありませんか。

シャットダウンフックを登録の逆方向に呼び出す方がわかりやすく、実際、C 実行時ライブラリの atexit 手続きは、登録の逆方向に呼び出されます。しかし、この方法は、シングルスレッドシステム以外では正常に機能しません。Java プラットフォームなどのマルチスレッドシステムの場合は、フックが登録された順番は通常定義されないため、フックの実行順を決定するための情報は存在しません。また、フックを特定の逐次順序で呼び出した場合、デッドロックが発生する可能性が高くなります。ただし、特定のサブシステム内で、特定の順序でシャットダウンアクションを呼び出す必要がある場合には、内部的にアクションを同期化することができます。

なぜフックは、起動されていないスレッドなのですか。Runnable オブジェクト、または Beans スタイルのイベントおよびリスナーパターンを使用する方が簡単ではありませんか。

Runnable オブジェクトまたは Beans スタイルのイベントリスナーに基づいて設計されたコールバック指向の方法は、わかりやすく一般によく使用されています。 しかし、ここで説明している方法には、次の 2 つの利点があります。

1 つ目の利点は、シャットダウンアクションが実行されるスレッドを、ユーザーが完全に制御できることです。ユーザーは、適切なスレッドグループにスレッドを作成し、スレッドに対して適切な優先順位、コンテキスト、特権などを割り当てることができます。

2 つ目の利点は、VM をフックから独立させることによって、仕様および実装が簡略化されます。シャットダウンアクションをコールバックとして実行する場合は、実装を安定させるために、フックごとに独立したスレッドを作成し、アクションを並列に実行する必要があります。また、コールバックを実行するスレッドの作成方法を、仕様に明示的に記述する必要があります。

スレッドを保持すると、特にシャットダウンするまで VM が起動しない場合は、負荷が大きくなりませんか。

多くの場合、Java プラットフォームの実装では、起動時までリソースはスレッドに割り当てられません。 このため、起動されていないスレッドを保持しても、実際には負荷は大きくありません。java.lang.Thread の内部を確認すると、各コンストラクタでは、セキュリティーチェックおよび private フィールドの初期化以外は行われていないことがわかります。ネイティブの start() メソッドでは、スレッドスタックの割り当てなどが行われてから処理が開始されます。

PersonalJavaTM および EmbeddedJavaTM はどうなりますか。シャットダウン中にスレッドを起動すると、これらのプラットフォームの場合、負荷が大き過ぎませんか。

この API は、小規模な Java プラットフォームには適切でないことがあります。Java 2 プラットフォームのスレッドには、JDK 1.1、PersonalJava、および EmbeddedJava のスレッドより多くの情報が保持されています。スレッドには、クラスローダが組み込まれています。 また、スレッドローカル変数が継承されることもあります。 GUI アプリケーションの場合には、特定のアプリケーションのコンテキストと関連付けられていることもあります。プラットフォームの拡張と共に、スレッドにはより多くの情報が保持されます。 たとえば、セキュリティーチームは、次のバージョンの認証フレームワークでは、スレッドごとのユーザー識別情報という概念を導入する予定です。

これらのコンテキスト情報を考慮すると、シャットダウンフックが Runnable オブジェクトまたは Beans スタイルのイベントリスナーの場合は、シャットダウンフックの作成および保守は困難です。たとえば、Runnable シャットダウンフックまたは同等のイベントリスナーでは、操作を実行するときに、スレッドのコンテキスト情報を保持するためのビットが必要であると仮定します。これらの情報は、フックが登録される前に共用位置に保存されます。この方法は複雑なうえ、今後のリリースで、新しい種類のコンテキスト情報がスレッドに保持されることも考慮する必要があります。フックから呼び出される操作が拡張されて、その情報を必要とする場合は、その情報を保存するために、フックを登録したコードも変更しなければなりません。フックを Runnable オブジェクトまたはイベントリスナーではなくスレッドとして実装することによって、このような今後の変更からフックを解放することができます。

簡単なシャットダウンフックを登録するときにも、多くのコードを記述する必要がありますか。

いいえ。次の例のように、多くの場合、簡単なシャットダウンフックは匿名の内部クラスとして記述できます。
Runtime.getRuntime().addShutdownHook(new Thread() {
    public void run() { database.close(); }
});
フックをキャンセルする必要がない場合は、このコードを使用できます。 キャンセルする必要がある場合は、作成時にフックへの参照を保存しなければなりません。

セキュリティーは確保されていますか。信頼されていないアプレットによってシャットダウンフックが登録される可能性はありますか。

セキュリティーマネージャーがインストールされている場合は、addShutdownHook メソッドおよび removeShutdownHook メソッドによって、呼び出し側のセキュリティーコンテキストから RuntimePermission("shutdownHooks") が与えられているかどうかが検査されます。信頼されていないアプレットには、このアクセス権が与えられていません。 このため、このアプレットから、シャットダウンフックの登録または登録解除を行うことはできません。

シャットダウンフックから例外がスローされ、その例外がキャッチされない場合はどうなりますか。

キャッチされなかった例外は、シャットダウンフックでは、ほかのスレッドの場合と同様に処理されます。 つまり、スレッドの ThreadGroup オブジェクトの uncaughtException メソッドが呼び出されます。このメソッドのデフォルトの実装によって、例外のスタックトレースが System.err に出力され、スレッドが終了します。例外がキャッチされなかった場合でも、VM は終了しません。 すべての非デーモンスレッドが終了したとき、または Runtime.exit メソッドが呼び出されたときには、VM が終了します。

なぜ Runtime.halt メソッドを追加したのですか。使用しても問題はありませんか。

この新しい halt メソッドは強力なので、使用するときには細心の注意が必要です。このメソッドは、シャットダウンフックがデッドロック状態か、予期しない時間実行されている場合に、アプリケーションから切り離すときに使用します。また、必要に応じて、アプリケーションをただちに終了させることもできます。

finalization-on-exit が有効な場合はどうなりますか。ファイナライザは、シャットダウンフックの実行前、実行中、実行後のどのタイミングで実行されますか。

finalization-on-exit 処理は、シャットダウンフックがすべて終了したあとに実行されます。そうでない場合、一部のライブオブジェクトが意図的にファイナライズされると、フックに失敗することがあります。

Copyright © 1999 Sun Microsystems, Inc. All Rights Reserved.

Sun
Java Software