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

次に示す質問と回答は、シャットダウン・フックAPIの設計上の問題に関するものです。

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

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

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

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

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

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

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

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

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

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

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

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

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

このAPIは、小規模なJavaプラットフォームには適切でないことがあります。Java 2 Platformのスレッドには、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 © 1993, 2020, Oracle and/or its affiliates. All rights reserved.