イベントディスパッチ スレッド(EDT) は、Abstract Window Toolkit (AWT)グラフィカル ユーザー インターフェイスイベント キューからのイベントを処理するためにJavaで使用されるバックグラウンドスレッドです。これは、 Web ブラウザーやWeb サーバーなど、 Java 以外の多くのコンテキストでよく使用されるイベント駆動型プログラミングの一般的な概念の一例です。
イベントは主に、ユーザーインターフェースコンポーネントを再描画させる更新イベント、またはマウスやキーボードなどの入力デバイスからの入力イベントです。 AWT はシングルスレッドのペイントモデルを使用しており、すべての画面更新は単一のスレッドから実行する必要があります。イベントディスパッチスレッドは、可視ユーザーインターフェースコンポーネントの視覚的状態を更新できる唯一の有効なスレッドです。他のスレッドから可視コンポーネントを更新すると、Swing を使用するJavaプログラムでよく発生するバグの原因となります。[1]イベントディスパッチスレッドは、 Adobe Flashでは原始ワーカー、SWT、.NET Framework、AndroidではUI スレッドと呼ばれます。
GUIアクセスをシリアル化するためのメッセージループ
ソフトウェアアプリケーションは通常、複数のスレッドと単一の GIT データ構造で構成されます。つまり、GIT は共有データ構造であり、一度に 1 つのスレッドのみがアクセスするように同期する必要があります。AWTとSwing は、 GUI コンポーネントを作成してアクセスするための(スレッド アンセーフ) メソッドを公開しており、これらのメソッドはすべてのアプリケーション スレッドから参照できますが、他の GUI フレームワークと同様に、単一のイベント ディスパッチ スレッドのみがこれらのメソッドを実行する権限を持ちます。[2] [3] [4] プログラマーはこの要件を見落とすことが多いため、Substance などのサードパーティのルック アンド フィールでは、イベント ディスパッチ スレッド内で実行されていない場合は Swing コンポーネントのインスタンス化を拒否し、[5]このようなコーディング ミスを防止しています。GUI へのアクセスはシリアル化され、他のスレッドはEDT メッセージ キューを介して EDT で実行されるコードを送信できます。
つまり、他の GUI フレームワークと同様に、イベント ディスパッチ スレッドはメッセージの送信に全力を尽くします。つまり、GUI 上で実行されるアクションのメッセージ キューを維持します。これらの要求は、システムおよび任意のアプリケーション スレッドによってキューに送信されます。EDT はそれらを次々に消費し、GUI コンポーネントを更新することで応答します。メッセージは、よく知られているアクションである場合もあれば、コールバック (EDT によって実行する必要があるユーザー メソッドへの参照) を伴う場合もあります。
すべてのメッセージに課せられる重要な要件は、GUI が応答し続けるためにはメッセージをすばやく実行する必要があるということです。そうしないと、メッセージ ループがブロックされ、GUI がフリーズしてしまいます。
EDT へのユーザーコードの送信
EDT にコードを送信し、ループをブロックせずに長いタスクを実行するためのさまざまなソリューションがあります。
コンポーネント イベント ハンドラー (リスナー)
GUI コンポーネントは、リスナーと呼ばれるコールバックのリストをサポートします。リスナーは通常、コンポーネントの作成時に設定されます。EDT は、ユーザーが何らかの方法でコンポーネントをアクティブにすると (ボタンがクリックされる、マウスが移動する、項目が選択される、フォーカスが失われる、コンポーネントのサイズが変更されるなど)、リスナーを実行します。
タイマー
定期的または特定の時間に GUI にアクセス/変更する必要がある短いタスクにjavax.swing.Timer使用されます。これは、特定の時間に起動するようにリスナーが登録されている非表示の GUI コンポーネントと考えることができます。
同等物
System.Windows.Forms.Timer- .NET フレームワークflash.utils.Timer-アドビフラッシュ
他のスレッドからのリクエスト
SwingUtilities他のアプリケーション スレッドは、ヘルパー クラス (またはAWT をEventQueue使用している場合)によって、イベント ディスパッチ スレッドで実行されるコードを渡すことができます。送信されたコードはオブジェクトでラップする必要があります。これらのクラスの 2 つのメソッドにより、次のことが可能になります。
Runnable
- 同期コード実行 (
SwingUtilities.invokeAndWait(Runnable)またはEventQueue.invokeAndWait(Runnable)) - 非同期コード実行(
SwingUtilities.invokeLater(Runnable)またはEventQueue.invokeLater(Runnable))
イベントディスパッチスレッドから。
このメソッドはinvokeAndWait()、イベント ディスパッチ スレッドからは決して呼び出されません。例外がスローされます。 メソッドSwingUtilities.isEventDispatchThread()またはEventQueue.isDispatchThread()を呼び出すと、現在のスレッドがイベント ディスパッチ スレッドであるかどうかを判断できます。
invokeLaterおよびによって EDT に提供されるコードは、invokeAndWaitフリーズを防ぐためにできるだけ高速である必要があります。通常、これらのコードは長時間の計算結果を GUI (ユーザー) に提供することを目的としています。
ワーカーデザインパターン
別のスレッドでのタスクの実行と EDT での結果の表示は、ワーカー デザイン パターンによって組み合わせることができます。Sun Microsystemsによって開発されたこのjavax.swing.SwingWorkerクラスは、ワーカー デザイン パターンの実装であり、Java 6 以降では標準の Swing ディストリビューションの一部となっています。SwingWorker は通常、EDT をブロックしないように長時間のタスクを実行するために、EDT 実行イベント リスナーから呼び出されます。
サンプル
SwingWorker < Document , Void > worker = new SwingWorker < Document , Void > () { public Document doInBackground () throws IOException { return loadXML (); // 重いタスク} public void done () { try { Document doc = get (); display ( doc ); } catch ( Exception ex ) { ex . printStackTrace (); } } }; worker . execute ();
Groovyと を使用する場合は、 、、 をgroovy.swing.SwingBuilder使用できます。次のようにもっと簡単に記述できます。
doLater()doOutside()edt()
doOutside { def doc = loadXML () // 重いタスクedt { display ( doc ) } }
同等物
System.ComponentModel.BackgroundWorker- .NET フレームワークflash.system.Worker-アドビフラッシュandroid.os.AsyncTask-アンドロイド
モーダル実行
SwingWorker は通常、コールバック (リスナー) イベントの処理中に EDT によって長時間のタスク用に作成されます。ワーカー スレッドを生成すると、EDT はワーカーが完了するのを待たずに現在のメッセージの処理を続行します。多くの場合、これは望ましくありません。
多くの場合、EDT は GUI コンポーネント アクションを処理します。このアクションでは、JFileChooser などの別のダイアログを使用してユーザーに選択を求めます。JFileChooser はポップアップ表示され、ユーザーがオプションを選択している間は応答し続けます。アクションは [OK] ボタンが押された後にのみ選択されたファイルで続行されます。これは時間がかかり (ユーザーは数秒で応答します)、EDT がブロックしている間 (ダイアログが閉じられ、現在のコンポーネント アクションが終了するまで、キュー内の新しいメッセージ (JFileChooser など) は処理されません)、応答性の高い GUI (メッセージは EDT に引き続き送信されます) が必要です。この悪循環は、EDT が新しいメッセージ ループに入ることで解消されます。このループでは、「モーダル ダイアログが終了しました」というメッセージが表示されるまで通常どおりメッセージが送信され、コンポーネント アクションのブロックされた位置から通常のメッセージ処理が再開されます。
オープンソースのFoxtrotプロジェクトは、Swing メッセージ ループ ポンピングをエミュレートして、ワーカーがタスクを完了した後にのみ続行される、任意のユーザー タスクの「同期」実行メカニズムを提供します。
button.addActionListener ( new ActionListener () { public void actionPerformed ( ActionEvent e ) { button.setText ( " Sleeping ... " ) ;
String text = null ; try { text = ( String ) Worker.post ( new Task () { public Object run () throws Exception { Thread.sleep ( 10000 ); return " Slept !" ; } } ) ; } catch ( Exception x ) ...
ボタン.setText (テキスト) ;
何か他のもの();
} });
Java 1.7 以降、Java はシステムEventQueue ()でcreateSecondaryLoop ()を公開することにより、カスタムセカンダリ メッセージ ループの標準ソリューションを提供します。
参照
参考文献
- ^ この問題は Java Swingに固有のものではありません。Windows Formsなど、ほとんどのウィジェット ツールキットで同じ問題が発生します。Windows Formsでは、BackgroundWorkerクラスが Java のSwingWorkerと同じ目的を果たします 。
- ^ 「イベントディスパッチスレッド」。Sun Microsystems 。 2011年10月2日閲覧。
- ^ 「Swing のデバッグ - 本当に難しいのか?」 Alexander Potochkin。2011 年 8 月 5 日時点のオリジナルよりアーカイブ。2011年 10 月 2 日閲覧。
- ^ 「Initial Threads」。Sun Microsystems 。 2011年10月2日閲覧。
- ^ 「Substance · Pushing Pixels における EDT 違反のより厳格なチェック」
