简体   繁体   中英

Using Java wait() and notify() without freezing Swing GUI

I understand that Swing GUIs themselves use Threads, but I am trying to use a separate thread to run my simulation. I created a class that implements Runnable and uses a custom Thread as most simple Runnable examples do. My run() method basically runs my simulation, updating every second (which works great), but I'm now trying to implement buttons that can pause/resume the simulation. My Start button successfully starts the thread, and my Pause button successfully pauses the thread. However, when Pause is selected, the entire GUI is paused, and you can see the button as still being selected and I am unable to select any buttons or interact with the GUI at all. As soon as I call wait() on my custom thread, my entire GUI halts, even though I'm using a Thread separate from this GUI. Why does calling wait() freeze up my GUI? How can I pause just this specific Thread and not the entire GUI?

Please note that the Start button should be what makes the program resume. Here's my code for the GUI:

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GridFrame extends JFrame {

    private static final long serialVersionUID = 2857470112009359285L;

    private MyGridPanel grid;
    private Simulation sim;

    GridFrame(Simulation sim, int w, int h, int rows, int cols) {
        this.sim = sim;
        setTitle("Simulation");
        setSize(w, h);
        grid = new MyGridPanel(w, h, rows, cols);
        add(grid, BorderLayout.CENTER);

        //Build bottom panel
        JPanel buttons = new JPanel();
        buttons.setLayout(new GridLayout(1,3));
        JButton start = new JButton("Start");
        JButton pause = new JButton("Pause");
        JButton reset = new JButton("Reset");

        start.setActionCommand("Start");
        start.addActionListener(new ButtonActionListener());
        pause.setActionCommand("Pause");
        pause.addActionListener(new ButtonActionListener());
        reset.setActionCommand("Reset");
        reset.addActionListener(new ButtonActionListener());

        buttons.add(start);
        buttons.add(pause);
        buttons.add(reset);

        add(buttons, BorderLayout.SOUTH);
    }

    public MyGridPanel getGrid(){
        return grid;
    }

    private class ButtonActionListener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {
            switch(e.getActionCommand()){
            case "Start":
                System.out.println("Start");
                sim.start();
                break;
            case "Pause":
                System.out.println("Pause");
                sim.pause();
                break;
            case "Reset":
                System.out.println("Reset");
                break;
            }

        }
    }
}

And here is my Runnable:

public class Simulation implements Runnable{

    private Thread t;
    private GridFrame frame;
    private boolean paused;

    public Simulation(){
        frame = new GridFrame(this, 300, 300, 10, 10);
        frame.setVisible(true);
        paused = true;
    }

    public void start () {
        if(t == null){
            //Thread has not been created. Simulation has not started to run yet
            System.out.println("Starting thread.");
            t = new Thread(this);
            t.start();
            paused = false;
        }
        else if(paused){
            //Simulation and thread already started to run but was paused. This should use notify() to resume the thread.
            resume();
            paused = false;
        }
    }

    public void resume(){
        synchronized(t){
            t.notify();
        }
    }

    public void pause(){
        synchronized(t){
            try {
                t.wait();
                paused = true;
            } catch (InterruptedException e) {
                System.out.println("Exception when trying to pause simulation");
                e.printStackTrace();
            }
        }
    }

    @Override
    public void run() {
        while(true){
            frame.getGrid().step();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted while simulation was running.");
                e.printStackTrace();
            }
        }

    }


    public static void main(String[] a) {
        Simulation s = new Simulation();
    }

}

Calling wait and notify on a Thread object behaves no differently than it does on any other object. Specifically, as you've noticed, it does not send a signal to the executing thread that it should pause, but rather it will block the calling thread (your UI thread in this case) until it receives a notify message.

You will need to implement a messaging system (such as a blocking queue) from your UI thread to your background thread in order to get what you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM