4

別のスレッドJListにあるのコンテンツを更新する必要があるプログラムがあります。コンテンツの数は時々変わる可能性があるので、更新時にDefaultListModelすべてのコンテンツをクリアして新しいコンテンツを追加するだけです。しかし、スレッドの更新中に更新DefaultListModelを開始する問題に遭遇したようです。JFrameこのような例外があります

Exception in thread "AWT-EventQueue-0" 
java.lang.ArrayIndexOutOfBoundsException: 3

これがコードの例です

    DefaultListModel model;
    JList jList;
    JScrollPane jScrollPane;

    Thread thread;
    public Frame() {
        this.setTitle("ASM_SIMULATOR");
        this.setBounds(100, 100, 500, 500);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.getContentPane().setLayout(null);

        model = new DefaultListModel();
        jList = new JList(model);
        jScrollPane = new JScrollPane(jList);

        jList.setBounds(50, 50, 300, 200);
        jScrollPane.setBounds(50, 50, 300, 200);

        this.getContentPane().add(jScrollPane);

        this.setVisible(true);

        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {

                    makeData();
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        });

        thread.start();
    }

    public void makeData() {
        System.out.println("makeData()");
        model.clear();

        for (int i = 0; i < 20; i++) {
            model.addElement((int) (Math.random() * 100));
        }

    }

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

    }
4

4 に答える 4

3

基本的な答えはしないでください

スイングはスレッドセーフではありません。

行う必要があるのは、aを使用SwingWorkerしてモデルを構築し、そのdone/processメソッドを使用してモデルをビューに適用するか、スレッドの使用SwingUtilities.invokeLaterを継続するために使用しますが、更新をイベントディスパッチスレッドに同期します。

詳細については、Swingの同時実行性をお読みください

于 2012-10-27T07:18:12.420 に答える
3

そのコードスニペットで、基本的な「すべてのSwingコンポーネントは、イベントディスパッチスレッド(= EDT)およびEDTのみでアクセス/変更する必要があります」に2回違反しています。

  1. メイン メソッドは、または同様のメソッドnew Frame()で呼び出しをラップする必要がありますSwingUtilities#invokeLater
  2. モデル更新スレッドは、バックグラウンド スレッドでモデルを変更します。モデルを更新すると、 によって受信されるイベントが発生しJListJList更新自体が行われます (これも間違ったスレッドで行われます)。

考えられる解決策は次の 2 つです。

  • バックグラウンド スレッドで新しいDefaultListModelを作成し、EDT で一度に置き換えます。
  • 既存のモデルを更新し続けますが、更新が EDT で行われることを確認してください。
于 2012-10-27T07:37:31.040 に答える
2
  1. Swingの同時実行性に問題があります

  2. に包む必要がありmodel.addElement((int) (Math.random() * 100));ますinvokeLater

  3. Thread正しい方法は、からワーカーを開始するRunnable#Threadか、を使用することですSwingWorker

  4. SwingWorkersメソッドpublish()からの出力でありprocess()EDT

于 2012-10-27T07:19:09.767 に答える
0

残念ながら、それはそれほど単純ではありません。GUI の更新は GUI スレッドのみに許可する必要があるため、他のスレッドはすべての更新を 経由で GUI スレッドに転送する必要がありますSwingUtilities.InvokeLater。あなたの場合、おそらくmakeDataメソッド全体をラップすることができます。これは、GUI を更新するだけなのでです。

    thread = new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {

                SwingUtilities.InvokeLater(new Runnable() {
                      public void run() {
                          makeData();
                      }
                });
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    });

のコードがmakeDataGUI スレッドで実行されることに注意してください。GUI を含まない他の時間のかかる作業を行っている場合InvokeLaterは、よりきめ細かい方法で使用して、UI スレッドをできるだけ自由に保つ必要があります。

編集: コードを注意深く見てみると、200 ミリ秒ごとに GUI を定期的に更新しているだけであることに気付きました。あなたはこれをもっと簡単に行うことができますjavax.swing.Timer

int delay = 200; //milliseconds
ActionListener taskPerformer = new ActionListener() {
    public void actionPerformed(ActionEvent evt) {
        makeData();
    }
};
new Timer(delay, taskPerformer).start();
于 2012-10-27T08:39:04.160 に答える