Fixing the Problem
Home ] Up ] Illustrating The Problem ] What's Happening? ] [ Fixing the Problem ]

 

 

The solution to this problem is to cause long-running work to be done in a separate thread.  

Here is a version of the above program that solves the problem:

package noHangThread;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

/**
 * This is a frame which solves the problems associated with trying to
 * do large amounts of work from within the Event Dispatch Thread.
 */

public class NoHangThreadExample extends JFrame
{
  //Construct the applet
  public NoHangThreadExample()
  {
    setTitle("No Hang Thread Example");
    setSize(200, 200);
    Container contentPane = getContentPane();
    contentPane.setLayout(new BorderLayout());
    contentPane.add( new ButtonPanel(), BorderLayout.CENTER );
    contentPane.add(m_progressLabel, BorderLayout.SOUTH);
  }
  
  private void doWork()
  {
    setProgress(false, "Working...");
    try
    {
      Thread.sleep(10000); // represents a long operation of some kind.
    }
    catch (InterruptedException ie)
    {
      ie.printStackTrace();
    }
    setProgress(true, "Done");
  }
  
  private void setProgress(final boolean enableButton, final String text)
  {
    SwingUtilities.invokeLater( new Runnable()
    {
      public void run()
      {
        m_clickMeButton.setEnabled(enableButton);
        m_progressLabel.setText(text);
      }
    }
    );
  }
  
  class ButtonPanel extends JPanel implements ActionListener
  {
    ButtonPanel()
    {
      setBackground(Color.blue);
      m_clickMeButton.addActionListener(this);
      add(m_clickMeButton);
      
      m_nowClickMeButton.addActionListener( new ActionListener()
      {
        public void actionPerformed(ActionEvent ev)
        {
          ButtonPanel.this.setBackground(Color.yellow);
        }
      }
      );
      m_nowClickMeButton.setEnabled(false);  // Disable for now
      add(m_nowClickMeButton);
    }
    
    public void actionPerformed(ActionEvent ev)
    {
      setBackground(Color.red);
      m_nowClickMeButton.setEnabled(true);
      Thread thread = new Thread()
      {
        public void run()
        {
          doWork();
        }
      };
      thread.start();
    }
  }
  
  public static void main(String[] args)
  {
    NoHangThreadExample frame = new NoHangThreadExample();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
  }
  
  private JButton m_clickMeButton = new JButton("Click me!");
  private JButton m_nowClickMeButton = new JButton("Now try to click me!");
  private JLabel m_progressLabel = new JLabel(" ", SwingConstants.CENTER);
}

Note that the only change is to invoke doWork() from a separate thread.

Try It!

Here is an applet that invokes the above code when you click on its "Start" button:

Your browser does not have Java support. Please install the Java Plug-in. Again, after you've brought the JFrame up, try clicking on the "Click me!" button, and see what actually happens!

Results

Did you notice that, now:

  • The "Click me!" button "springs back" immediately?
  • The background color changes to red immediately?
  • The "Now try to click me!" button is now enabled immediately?
  • The whole GUI does not freeze?
 
The page was last updated February 19, 2008