2

アプリケーションでのスレッドの処理に問題があります。JFrame を作成し、新しいスレッドを開始します。最後の 1 つは、外部アプリケーションを実行し、GUI を更新します。それで

Main クラスを 2 番目のスレッドが終了するまで待機させるだけでなく、同時に GUI を更新するという問題があります。

これが私の例です(短縮):

class Main {

    public int status;

    public Main() {

        // Creating GUI etc.

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                JDialog id = new JDialog();
                id.button.addMouseListener(new MouseListener()); // Calls generate() method
            }

        });

    }

    public void generate() {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                // Make changes to GUI
            }

        });

        GeneratorThread genTest = new GeneratorThread(this, 1, 1, 1);
        genTest.start();

        //while (status == 0);

        System.out.println("Next step.");

    }

}

そしてスレッドクラス:

public class GeneratorThread extends Thread {

protected Main main;
protected int setSize, minValue, maxValue;

public GeneratorThread(Main main, int setSize, int minValue, int maxValue) {
    this.main = main;
    this.setSize = setSize;
    this.minValue = minValue;
    this.maxValue = maxValue;
}

public void run() {

    // Execute program etc.
    // Change GUI from main in the same time
            // About 3 seconds

    main.status = 1;

}

}

私は進行中であり、これまでのところどのように機能するかを確認したかったのです。うまく機能していましたが、何らかの形で Swing をロックし、変更はGeneratorThread終了時にのみ表示されます。GUIをリアルタイムで更新したい。

試してみましたがjoin()、効果は同じです。wait()私も(で)試しましMainたが、IllegalStateMonitorExceptionが発生しました。

ヒントはありますか?

4

2 に答える 2

1

SwingUtilitied.invokeLaterを使用してスレッド内からGUIを更新するか、またはメイン変数を同期します。

http://www.vogella.com/articles/JavaConcurrency/article.html#concurrencyjava

たぶん、「ステータス」を揮発性にすることですでに十分ですか?

于 2013-01-08T18:41:50.400 に答える
1

Swing はシングルスレッド環境です。つまり、Swing UI へのすべての対話と更新を管理する単一のスレッド、つまりイベント ディスパッチ スレッドがあります。

Swing の黄金律には次のようなものがあります。

  • EDT をブロックしないでThread.sleepください(ブロック IO および/または時間のかかるタスク (特に) は、EDT 内から呼び出されるべきではありません)。そうすると、EDT がイベントをディスパッチし、更新を描画するのを停止します (特に)Thread#joinObject#wait
  • EDT 内からのみ Swing UI 要素を作成/更新します。

ここで疑問が生じます...どうやってスレッドを「待つ」のですか?

最善の方法は、Observer パターンを使用することです。基本的に、Threadエラーや完了などのイベントの通知を提供するために呼び出す何らかの参照を に提供します...

これには、コードの単純な A から B への実行に依存できないため、アプリケーションの設計について非常に慎重に検討する必要があります。

例えば...

public class TestThreadCallBack {

    public static void main(String[] args) {
        new TestThreadCallBack();
    }

    public TestThreadCallBack() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface ThreadCallBack {

        public void threadCompleted(Runnable source);

        public void threadFailed(Runnable source);
    }

    public class TestPane extends JPanel implements ThreadCallBack {

        private JLabel message;
        private JLabel dots;
        private int count;

        private Timer timer;

        public TestPane() {
            setLayout(new GridBagLayout());
            message = new JLabel("Running background task, please wait");
            dots = new JLabel("   ");
            add(message);
            add(dots);

            timer = new Timer(250, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    count++;
                    if (count > 3) {
                        count = 0;
                    }
                    StringBuilder sb = new StringBuilder(3);
                    for (int index = 0; index < count; index++) {
                        sb.append(".");
                    }
                    for (int index = count; index < 3; index++) {
                        sb.append(" ");
                    }
                    dots.setText(sb.toString());
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();

            Thread thread = new Thread(new BackgroundTask(this));
            thread.start();

        }

        @Override
        public void threadCompleted(Runnable source) {
            timer.stop();
            message.setText("Task completed successfully");
        }

        @Override
        public void threadFailed(Runnable source) {
            timer.stop();
            message.setText("Task failed");
        }
    }

    public class BackgroundTask implements Runnable {

        private ThreadCallBack callBack;

        public BackgroundTask(ThreadCallBack callBack) {
            this.callBack = callBack;
        }

        @Override
        public void run() {
            System.out.println("Background task underway...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException interruptedException) {
            }
            int result = (int) Math.round((Math.random() * 1));
            if (result == 0) {
                callBack.threadCompleted(this);
            } else {
                callBack.threadFailed(this);
            }
        }
    }
}

Thread別の EDT内から UI を更新するのは、面倒です。より簡単な解決策は、実際にはSwingWorker. これには、UI の更新を容易にする発行/処理メソッドと、現在のタスクの進行状況に関するフィードバックを提供するために使用できる進行メソッドがあります。

そのメソッドを使用してdone、ワーカーが完了したときに関係者に通知できます。

于 2013-01-08T22:44:43.397 に答える