4

I've hit the infinite loop problem in Swing. Done some research and come across SwingWorker threads but not really sure how to implement them. I've knocked together a simple program that shows the problem. One button starts the infinite loop and I want the other button to stop it but of course due to the Swing single thread problem the other button has frozen. Code below and help appreciated:-

public class Model
{
    private int counter;
    private boolean go = true;

    public void go()
    {
        counter = 0;

        while(go)
        {
            counter++;
            System.out.println(counter);
        }
    }

    public int getCounter()
    {
        return counter;
    }

    public void setGo(boolean value)
    {
        this.go = value;
    }
}

public class View extends JFrame
{
    private JPanel                  topPanel, bottomPanel;
    private JTextArea               messageArea;
    private JButton                 startButton, cancelButton;
    private JLabel                  messageLabel;
    private JScrollPane             scrollPane;

    public View()
    {
        setSize(250, 220);
        setTitle("View");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        topPanel = new JPanel();
        bottomPanel = new JPanel();
        messageArea = new JTextArea(8, 20);
        messageArea.setEditable(false);
        scrollPane = new JScrollPane(messageArea);
        messageLabel = new JLabel("Message Area");
        topPanel.setLayout(new BorderLayout());
        topPanel.add(messageLabel, "North");
        topPanel.add(scrollPane, "South");
        startButton = new JButton("START");
        cancelButton = new JButton("CANCEL");
        bottomPanel.setLayout(new GridLayout(1, 2));
        bottomPanel.add(startButton);
        bottomPanel.add(cancelButton);
        Container cp = getContentPane();
        cp.add(topPanel, BorderLayout.NORTH);
        cp.add(bottomPanel, BorderLayout.SOUTH);
    }

    public JButton getStartButton()
    {
        return startButton;
    }

    public JButton getCancelButton()
    {
        return cancelButton;
    }

    public void setMessageArea(String message)
    {
        messageArea.append(message + "\n");
    }
}


public class Controller implements ActionListener
{
    private Model theModel;
    private View  theView;

    public Controller(Model model, View view)
    {
        this.theModel = model;
        this.theView = view;
        view.getStartButton().addActionListener(this);
        view.getCancelButton().addActionListener(this);
    }

    public void actionPerformed(ActionEvent ae)
    {
        Object buttonClicked = ae.getSource();
        if(buttonClicked.equals(theView.getStartButton()))
        {
            theModel.go();
        }
        else if(buttonClicked.equals(theView.getCancelButton()))
        {
            theModel.setGo(false);
        }
    }
}



public class Main
{
    public static void main(String[] args)
    {
        Model model = new Model();
        View view = new View();
        Controller controller = new Controller(model, view);
        view.setVisible(true);
    }
}
4

4 に答える 4

3

Event Dispatch Thread(EDT)をブロックしています。スレッドは、ペイントやその他の UI 関連の要求を処理する責任があります。EDT がブロックされると、イベントを処理できないため、UI がフリーズします。詳細については、イベント ディスパッチ スレッドのチュートリアルを参照してください。

タイマー ( How to Use Swing Timers )、SwingWorkerまたは補助的なバックグラウンド スレッドの使用を検討してください。バックグラウンド スレッドは、 を使用して EDT と通信できますSwingUtilities.invokeLater()。このメカニズムはすでに に実装されてSwingWorkerいるため、それを使用する方が簡単かもしれません。必要な機能によって異なります。

于 2012-11-10T23:32:40.407 に答える
3

タイマーを実装しなくても簡単に実行できます。actionPerformedメソッドに次の 2 行を追加するだけです。

public void actionPerformed(ActionEvent ae)
{
    Object buttonClicked = ae.getSource();
    if(buttonClicked.equals(theView.getStartButton()))
    {
      theModel.setGo(true); //make it continue if it's just stopped
      Thread t = new Thread(new Runnable() { public void run() {theModel.go();}}); //This separate thread will start the new go...
      t.start(); //...when you start the thread! go!
    }
    else if(buttonClicked.equals(theView.getCancelButton()))
    {
        theModel.setGo(false);
    }
}

Model.go ()は別のスレッドで実行されているため、イベント ディスパッチ スレッドは、ボタンを押したままハングするのではなく、ボタンを再び離して描画するなど、自由に処理を実行できます。

キャッチがあります!ただし、 Model.go()を実行しているスレッドが暴走するためです。、それは事実上、システムができる限り毎秒何回も呼び出されます。

アニメーションなどを実装する予定がある場合は、次のことを行う必要があります。

  • タイマーを使用し、

また

スレッドを使用する場合の例:

public void go()
{
    counter = 0;
        while(go)
    {
        counter++;
        System.out.println(counter);
        try {
            Thread.sleep(1500); //Sleep for 1.5 seconds
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ご覧のとおり、 Thread.sleep(1500)を 1500 ミリ秒 (1.5 秒) で追加しました。Thread.sleep は何らかの理由で中断される可能性があるため、InterruptedExceptionをキャッチする必要があります。

この特定のケースでは、 InterruptedExceptionを正しく処理するために深く掘り下げる必要はありませんが、興味がある場合は、この素晴らしい記事を読むことができます。

于 2012-11-11T00:49:27.253 に答える
0

私は SwingWorker スレッドを使用することにしました。以下は、更新された Controller クラスです。それは私が必要とすることをしますが、私の質問は、それは正しい方法ですか、それはきれいなコードですか? また、コメントアウトされた行に従って、model.go() メソッドの出力をビューのテキストエリアに取得しようとしましたが、成功しませんでした。

public class Controller implements ActionListener
{
private Model theModel;
private View  theView;
private SwingWorker<Void, Void> worker;

public Controller(Model model, View view)
{
    this.theModel = model;
    this.theView = view;
    view.getStartButton().addActionListener(this);
    view.getCancelButton().addActionListener(this);
}

public void actionPerformed(ActionEvent ae)
{
    Object buttonClicked = ae.getSource();
    if(buttonClicked.equals(theView.getStartButton()))
    {
        theModel.setGo(true);
        worker = new SwingWorker<Void, Void>()
        {
            @Override
            protected Void doInBackground()
            {
                // theView.setMessageArea(theModel.getCounterToString());
                return theModel.go();
            }
            @Override
            protected void done()
            {
                // theView.setMessageArea(theModel.getCounterToString());
            }
        };
        worker.execute();

    }
    else if(buttonClicked.equals(theView.getCancelButton()))
    {
        theModel.setGo(false);
    }
}
}


public class Model
{
    public Void go()
    {
        counter = 0;

        while(go)
        {
            counter++;
            System.out.println(counter);
        }
        return null;
    }
于 2012-11-11T16:22:53.737 に答える