Search code examples
javamultithreadingbackgroundworkerswingworkerevent-dispatch-thread

How to make EDT wait a background thread?


Based on Oracle Concurrency guidelines I'm trying to make an update of my GUI. My problem is, the GUI update is being called in a new Thread within EDT and that is causing my a lot of different failures.

This code below is ready to run and its a sample of me GUI update, a simple count down:

import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class Chronometer2 {

int COUNT = 5000;


CountTask countTask;


    private static final Insets INSETS = new Insets(1, 1, 1, 1);


    public Chronometer2(){

        (countTask= new CountTask()).execute();

    }


    private static void addComponent(Container container, Component component, int gridx, int gridy,
            int gridwidth, int gridheight, int anchor, int fill) {

          GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0,
        anchor, fill, INSETS, 0, 0);

          container.add(component, gbc);
          }

    public int returnCount(){

        System.out.println("My count: " + COUNT);
        return COUNT;
    }

       public void decreaseTime() {
            COUNT--;
        }

        public int getTime() {
            return COUNT;
        }


        private class CountTask extends SwingWorker<Void, Chronometer2>{

            @Override
            protected Void doInBackground() throws Exception {
                // TODO Auto-generated method stub



                final JFrame messageFrame = new JFrame();
                final JLabel message = new JLabel("Next query in " + COUNT/1000 + " seconds.");
                messageFrame.setLayout(new GridBagLayout());

                messageFrame.setTitle("Warning");
                messageFrame.setSize(500,100);
                messageFrame.setLocationRelativeTo(null);
                addComponent(messageFrame, message, 0, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.CENTER);
                messageFrame.setVisible(true);

                final Timer timer = new Timer();
                 timer.schedule(new TimerTask() {

                     @Override
                     public void run() {


                             decreaseTime();
                                    message.setText("Preventing connection block. Next query in " + COUNT/1000 + " seconds.");

                                    if(getTime()==0){
                                        messageFrame.dispose();
                                        timer.cancel();
                                    }

                                }
                 }, 1, 1);
                return null;
            }

        }




public static void main(String[] args){
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new Chronometer2();
           }
       });
}

}

Since the sample is called from the main Thread, its working normally. But from my application the call is being made inside EDT because I need to push a button and trigger an action before show the message. I made one new Thread to invoke the Swing utilities (which can not be call directly inside EDT) and even though I'm having problems because I couldn't make until now the EDT wait the background thread to be done and then move on to the next query. This is the stretch of code inside the EDT:

Thread test = new Thread(new Runnable() {

                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            try {
                                SwingUtilities.invokeAndWait(new Runnable() {

                                    @Override
                                    public void run() {
                                        // TODO Auto-generated method stub
                                        new CountScreen();
                                    }



                                });
                            } catch (InvocationTargetException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    });
                    test.start();

I also tryed to handle this with wait(), notifiy() and Timer Task but the problem is the same; Here is the other code ready to run:

import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JLabel;

public class Chronometer {


    private static final Insets INSETS = new Insets(1, 1, 1, 1);

    private int TIMER = 3000;

public Chronometer(){

    double timeToNext = System.currentTimeMillis() + 30000;
        double timeDifference = 30000;

    final JFrame messageFrame = new JFrame();
    messageFrame.setLayout(new GridBagLayout());

    messageFrame.setTitle("Warning");
    final JLabel message = new JLabel("Next query in " + timeDifference/1000 + " seconds.");
    messageFrame.setSize(500,100);
    messageFrame.setLocationRelativeTo(null);
    addComponent(messageFrame, message, 0, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.CENTER);
    messageFrame.setVisible(true);

    System.out.println(Thread.currentThread().getName());

     final Timer timer = new Timer();
     timer.schedule(new TimerTask() {

         @Override
         public void run() {



                synchronized (timer) {


                     decreaseTime();
                        message.setText("Preventing connection block. Next query in " + TIMER/1000 + " seconds.");

                        if(getTime()==0){

                            System.out.println("Im here");
                            timer.notify();
                            System.out.println("Im here 2");
                            messageFrame.dispose();
                            System.out.println("Im here 3");
                            timer.cancel();
                        }

                    }
             }
     }, 1, 1);

    synchronized (timer) {

       try {

            timer.wait();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

       System.out.println("Done");

 }



private static void addComponent(Container container, Component component, int gridx, int gridy,
        int gridwidth, int gridheight, int anchor, int fill) {

      GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0,
    anchor, fill, INSETS, 0, 0);

      container.add(component, gbc);
      }

public void decreaseTime() {
    TIMER--;
}

public int getTime() {
    return TIMER;
}


public static void main(String[] args){

Chronometer c = new Chronometer();
}

}

So, I have almost 3 days wondering, how can I solve this? Any suggestions are very welcome; Thanks in advance.


Solution

  • I found my own answer. Stop the EDT will never be an option. NEVER. If you wanna handle one problem like mine the solution is pretty much simpler than I thought. Right after trigger the action in the listener just call another thread to handle whatever you want. This you prevent the EDT to become busy and freezes the GUI. This way, you will be able to lock the Thread in many ways as possible as you can, and by that I mean, sync methods, loops or what else you wanna use. My solution I could think reading this article: main and the GUI Event Dispatch. It's really an awesome article that opened my mind for a new stuff regarding to swing and threads. Hope this becomes helpful to you too.