AWTのスレッドの問題
リスナーとスレッド
特に断りのないかぎり、すべてのAWTリスナーはイベント・ディスパッチ・スレッド上で通知されます。 ディスパッチ中に任意のスレッドからリスナーを追加/削除しても安全ですが、変更はその後の通知のみに影響します。たとえば、キー・リスナーが別のキー・リスナーから追加された場合、新しく追加されたリスナーにはその後のキー・イベントのみが通知されます。
自動シャットダウン
「Java™仮想マシン仕様」セクション2.17.9および2.19によれば、Java仮想マシン(JVM)は最初は単一の非デーモン・スレッドで起動し、通常このスレッドが何らかのクラスのmain
メソッドを呼び出します。 仮想マシンは、次の2つのうちのいずれかが発生した場合にすべてのアクティビティを終了し、仮想マシン自身を終了します。
- 非デーモン・スレッドがすべて終了した場合。
- あるスレッドが、クラス
Runtime
またはクラスSystem
のexit
メソッドを呼び出し、セキュリティ・マネージャによってexit動作が許可された場合。
これは、アプリケーション自身がスレッドを開始しない場合、JVMはmain
が終了するとただちに終了することを意味します。 ただし、java.awt.Frame
を作成して表示する単純なアプリケーションの場合にはこれは当てはまりません。
public static void main(String[] args) { Frame frame = new Frame(); frame.setVisible(true); }その理由は、AWTが、AWTまたはSwingコンポーネントがトリガーできるイベントを処理するために、非同期イベント・ディスパッチ機構をカプセル化するからです。 この機構の正確な動作は実装によって異なります。 具体的には、内部的な目的で非デーモンのヘルパー・スレッドを開始できます。 実際に、上記の例ではこれらのスレッドが終了を妨げています。 この機構の動作に適用される制限は、次のもののみです。
-
EventQueue.isDispatchThread
は、呼出し元スレッドがこの機構によって開始されたイベント・ディスパッチ・スレッドである場合にかぎりtrue
を返します。 - 特定の
EventQueue
(EventQueue
に送られたイベントは合体できる)に実際に入れられたAWTEvents
は次のようにディスパッチされます。-
- 順次
- これが指定されている場合、このキューの複数イベントの同時ディスパッチは許可されません。
-
- キューに入れられた順序
-
AWTEvent
AがAWTEvent
Bよりも前にEventQueue
に入れられた場合、イベントBをイベントAよりも前にディスパッチすることはできません。
-
- アプリケーション内に少なくとも1つの表示可能なAWTまたはSwingコンポーネントがある間は、少なくとも1つの非デーモン・スレッドが生存しています(
Component.isDisplayable
参照)。
- 任意のスレッドがクラス
Runtime
またはクラスSystem
のexit
メソッドを起動した場合、JVMは表示可能なコンポーネントが存在するかどうかにかかわらず終了します。 - アプリケーションが自身で開始した非デーモン・スレッドをすべて終了した場合でも、少なくとも1つの表示可能なコンポーネントが存在するかぎり、JVMは終了しません。
実装依存の動作。
1.4より前では、ヘルパー・スレッドは決して終了しませんでした。1.4から、4030718の修正の結果、この動作は変更されました。 現在の実装では、次の3つの条件が満たされた場合、AWTはそのすべてのヘルパー・スレッドを終了し、アプリケーションが正常に終了できるようにします。
- 表示可能なAWTまたはSwingコンポーネントが存在しない。
- ネイティブ・イベント・キューにネイティブ・イベントが存在しない。
- javaのEventQueueにAWTイベントが存在しない。
System.exit
を呼び出さずに正常に終了したいスタンドアロンAWTアプリケーションは、次を確認する必要があります。
- アプリケーションの終了時に、すべてのAWTまたはSwingコンポーネントが表示不可になっていること。 これは、すべてのトップ・レベルの
Windows
でWindow.dispose
を呼び出すことにより実行できます。Frame.getFrames
を参照してください。 - アプリケーションによってAWTまたはSwingコンポーネントに登録されたAWTイベント・リスナーのメソッドが、無限ループに入ったり無期限にハングしたりしないこと。 たとえば、AWTイベントによってトリガーされたAWTリスナーのメソッドが、同じ型の新しいAWTイベントを
EventQueue
に送信することがあります。 問題は、AWTイベント・リスナーのメソッドは通常ヘルパー・スレッドで実行されることです。
- ほかのパッケージが、内部的な必要性のために表示可能なコンポーネントを作成して表示不可にしない場合。 4515058、4671025、および4465537を参照してください。
- Microsoft WindowsとX11はいずれも、アプリケーションがネイティブ・イベントをほかのアプリケーションに属するウィンドウに送信することを許可します。 この機能を使用すると、使用可能なすべてのウィンドウにイベントを送信し続けてAWTアプリケーションが正常に終了することを妨げるような、悪意のあるプログラムを作成できます。
<...> Runnable r = new Runnable() { public void run() { Object o = new Object(); try { synchronized (o) { o.wait(); } } catch (InterruptedException ie) { } } }; Thread t = new Thread(r); t.setDaemon(false); t.start(); <...>「Java™仮想マシン仕様」では、このスレッドが終了するまでJVMが終了しないことが保証されています。