title: Using Java wait() and notify() without freezing Swing GUI
body: <p>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. <strong>Why does calling wait() freeze up my GUI? How can I pause just this specific Thread and not the entire GUI?</strong></p>
  
  <p>Please note that the Start button should be what makes the program resume. Here's my code for the GUI:</p>
  
  <pre><code>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;
              }
  
          }
      }
  }
  </code></pre>
  
  <p>And here is my Runnable:</p>
  
  <pre><code>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();
      }
  
  }
  </code></pre>