4

I'm making a simple game in Java using swing and am having problems with my GUI freezing up sporadically (due to threading issues most probably) after a button press that is supposed to trigger a switch in JPanels.

I posted a related thread here, which has more details about the actual code I'm currently using (although I did update the countdown and get that working fine). From answers to that thread, it seems like usingSwingUtilities.invokeLater() or invokeAndWait() might be what I need to solve the problem, but I'm not sure where in my code it is necessary or exactly how to implement it.

I don't know that much about threading and could use any help (preferably somewhat detailed and with some sample code) that I can get. Let me know if any further details would be useful.

Community
  • 1
  • 1
scaevity
  • 3,991
  • 13
  • 39
  • 54

4 Answers4

4

See: Tutorial: Concurrency in Swing

Generally speaking, the Event Dispatch Thread is a single thread, chugging through the event queue, processing one at a time.

SwingUtilities.invokeLater(..) 

puts a Runnable on this queue. So it will be processed by the EDT when the EDT finishes everything on the queue before it (This is why sleeping on the queue blocks other events like repainting). It's relatively unusual to call invokeLater(..) from the EDT itself, though there are situations where it is useful (usually as a hack). I don't think I have had a legitimate use for SwingUtilities.invokeAndWait(..) in the last 6 years. Maybe once.

javax.swing.Timer can be configured to fire once or periodically. When it fires, it puts an event on the EDT queue. If you have computationally-intensive processing that need to be done, consider using javax.swing.SwingWorker to do the computation on another thread, and give you back the result in a thread-safe manner (this is also comparatively rare).

kylewm
  • 634
  • 6
  • 21
  • Thanks for the explanation. Could you help me figure out what is wrong in my code in specific, though, and how to fix it (I'm not quite sure where the EDT starts/ends and where I'd need to use an invokeLater() to add something to the EDT queue)? – scaevity Feb 29 '12 at 22:56
  • 2
    Try creating a SSCCE (http://sscce.org/) that demonstrates your problem. Distilling the problem may help you solve it on your own, otherwise, post it here and people will be able to give you more specific advice. – kylewm Feb 29 '12 at 23:10
  • thanks! I did that and it turns out it was something that I'd thought was entirely unrelated (non GUI stuff) that was causing the freeze. – scaevity Mar 01 '12 at 01:53
1

A good point to look is the docs. In your case, this explains how SwingUtilities.invokeLater() works and where to use it:

Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread. This method should be used when an application thread needs to update the GUI.

So, in your actions that modifies the GUI you must use the invokeLater method to assure that the GUI wont freeze.

Another good resource is the Java tutorials. They cover concurrency in Swing.

  • ok, I understand that I will need to use invokeLater() to stop the freezing by adding some bit of code to the EDT queue, but where in my code do I need to do this? I experimented with adding it to a few different places that I thought would make sense, but none of my ideas fixed it so I'm obviously missing something (for code samples/program outline look at my other post). – scaevity Feb 29 '12 at 22:59
0

I have create a WorkerThread class which take care of Threads and GUI current/main thread . i have put my GUI application in construct() method of WorkerThread when an event fire to start XXXServer then all threads are activate and GUI work smoothlly wihout freeze. have a look. /** * Action Event * * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent ae) { log.info("actionPerformed begin..." + ae.getActionCommand());

    try {
        if (ae.getActionCommand().equals(btnStart.getText())) {
             final int portNumber = 9990;
             try {

                 WorkerThread workerThread = new WorkerThread(){
                    public Object construct(){

                        log.info("Initializing the XXXServer ...");
                        // initializing the Socket Server
                         try {
                            XXXServer xxxServer = new XXXServer(portNumber);
                            xxxServer.start();
                            btnStart.setEnabled(false);                             
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
                            e.printStackTrace();
                        }
                        return null;
                    }
                };workerThread.start();
                } catch (Exception e) {
                    log.info("actionPerformed() Start button ERROR..." + e.getMessage());
                    e.printStackTrace();
             }


        } else if (ae.getActionCommand().equals(btnStop.getText())) {
            log.info("Exit..." + btnStop.getText());
            closeWindow();
        }

    } catch (Exception e) {
        log
            .info("Error in ServerGUI actionPerformed==="
                + e.getMessage());
    }

}
mkumar0304
  • 637
  • 7
  • 8
0

In order to invoke an action in the existing WorkerThread, one would intuitively send a user defined event using SwingUtilities.invokeLater() to a JFrame's actionPerformed() method as

class TestFrame extends JFrame implements ActionListener
{
...

    private class Performer implements Runnable
    {
        ActionEvent event;

        Performer(ActionEvent event)
        {
            this.event = event;
        }

        @Override
        public void run()
        {
            actionPerformed(event);
        }

    }

    synchronized protected void invokeLater(ActionEvent event)
    {
        SwingUtilities.invokeLater(new Performer(event));
    }

    public void actionPerformed(ActionEvent event)
    {
        ...
    }

}

Now, TestFrame.invokeLater() called in any Thread will be processed in TestFrame.actionPerformed() in existing WorkerThread .

Sam Ginrich
  • 661
  • 6
  • 7