Javax.swingを使用した以下のサンプルGUIプログラムの場合、
_public class UnResponsiveUI extends JFrame{
private boolean stop = false;
private JTextField tfCount;
private int count = 1;
/* Constructor to setup the GUI components */
public UnResponsiveUI(){
Container cp = this.getContentPane();
cp.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
cp.add(new JLabel("Counter"));
tfCount = new JTextField(count + "", 10);
tfCount.setEditable(false);
cp.add(tfCount);
JButton btnStart = new JButton("Start Counting");
cp.add(btnStart);
btnStart.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
stop = false;
for (int i = 0; i < 100000; ++i) {
if (stop) break; // check if STOP button has been pushed,
// which changes the stop flag to true
tfCount.setText(count + "");
++count;
}
}
});
JButton btnStop = new JButton("Stop Counting");
cp.add(btnStop);
btnStop.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent evt) {
stop = true; // set the stop flag
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Counter");
setSize(300, 120);
setVisible(true);
}
public static void main(String[] args){
/*SwingUtilities.invokeLater(new Runnable(){
public void run() {
new UnResponsiveUI(); // Let the constructor do the job
}
});*/
/* new UnResponsiveUI(); */
System.out.println("main thread exits");
}
}
_
最初のシナリオでは、main()から呼び出されたコンストラクタをnew UnResponsiveUI();
としてデバッグした後、起動されたスレッドの数に関する観察結果を次に示します。
1。 main()メソッドは「メイン」スレッドで開始します。
2。新しいスレッド「AWT-Windows」(デーモンスレッド)は、コンストラクター「new UnresponsiveUI()」にステップインすると開始されます(「JFrameを拡張するため」)。
3。 「setVisible(true)」を実行すると、別の2つのスレッド「AWT-Shutdown」と「AWT-EventQueue-0 "(つまり、EDT)が作成されます
4。 「メイン」スレッドは、main()メソッドが完了すると終了します。 「DestroyJavaVM」という新しいスレッドが作成されます。
5。この時点で、「AWT-Windows」、「AWT-Shutdown」、「AWT-EventQueue EDT)」、「DestroyJavaVM」の4つのスレッドが実行されています。
6。 STARTボタンをクリックすると、EDTでactionPerformed()が呼び出されます。
2番目のシナリオでは、次のように呼び出されたmain()からコンストラクターをデバッグした後、
_SwingUtilities.invokeLater(new Runnable(){
public void run() {
new UnResponsiveUI(); // Let the constructor do the job
}
});
_
以下は、起動されたスレッド数に関する観察ですが、インスタンスが異なる場合があります。
1。 main()メソッドは「メイン」スレッドで開始されます。
2。 JREのウィンドウサブシステムは、
SwingUtilities.invokeLater()
を介して、「AWT-Windows」(デーモンスレッド)、「AWT-Shutdown」、および「AWT-EventQueue-0」の3つのスレッドを同時に開始します。 「AWT-EventQueue-0」は、イベントディスパッチスレッド(EDT)として知られています。これは、すべてのイベント(ボタンのクリックなど)を処理し、表示を更新してGUIでスレッドの安全性を確保する唯一のスレッドです。操作とGUIコンポーネントの操作3。次に、コンストラクター
UnresponsiveUI()
にステップインして、既存のすべてのイベントが処理された後、(invokeLater()
を介して作成された)イベントディスパッチスレッドで実行します。4。 「メイン」スレッドは、main()メソッドが完了すると終了します。
5。 「DestroyJavaVM」という新しいスレッドが作成されます。
したがって、上記の2つのシナリオでは、作成されるスレッドの総数は同じですが、インスタンスが異なり、GUIコンストラクターが同じEDTで実行されます。
この---(query から、「invokeLaterを使用せず、UIを直接更新しただけの場合、競合状態が発生し、未定義の動作が発生する可能性がある」と学びました。
私の質問:
SwingUtilities.invokeLater()
メソッドを使用する利点は何ですか? 2番目のシナリオでは、コンストラクターにステップインする前にEDTを起動しても値が追加されなかったためです。
SwingUtilities.invokeLater()を使用することは、単に有利であるだけでなく、不可欠ですが、Swingのスレッドモデルを理解する必要がある理由を理解するために不可欠です。
Swingを介したGUIの更新は、イベントディスパッチスレッド(EDT)で行う必要があり、その他のことを行うコード(データベースなどのリソースへのアクセスなど)は、1つ以上の他のスレッドを使用する必要があります。
invokeLater()は、これらの他のスレッドがEDT内の結果でGUIを更新できるようにします。データベースクエリの結果を表示します。
invokeLater()は、EDTがビジー状態になっている可能性があるため、指定したRunnableオブジェクトを呼び出す際に非同期であることを明確にするために、このように名前が付けられています。明示的にではありませんwant後で発生します。