Query on creating separate thread in java?

596 views Asked by At

Below is the compiled program replica of actual problem code,

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class Dummy {

    public static boolean getUserCheck(int size, boolean Check) {
        if (Check) {
            int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
                    "Warning", 0);
            if (ret > 0) {
                System.out.println("User said No: " + ret);
                return false;
            } else if (ret <= 0) {
                System.out.println("user said Yes: " + ret);
                return true;
            }
        }
        return true;
    }

    public static void workerMethod1() {
        System.out.println("am worker method 1");
    }

    public static void workerMethod2() {
        System.out.println("am worker method 2");
    }

    public static void main(String[] args) {
        System.out.println("mainthread code line 1");
        int size = 13;
        boolean thresholdBreach = true;

        if (getUserCheck(size, thresholdBreach)) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    workerMethod1();
                }
            });

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    workerMethod2();
                }
            });
        }
        System.out.println("mainthread code line 2");
        System.out.println("mainthread code line 3");
    }
}

where i would like to run the if{} block in main() on separate thread. Because these 2 lines,

        System.out.println("mainthread code line 2");
        System.out.println("mainthread code line 3");

need not wait for completion of if(){} block

Another problem is, experts recommend to run confirm-dialog methods on event thread.

int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
                    "Warning", 0);

Please help me!!!!

2

There are 2 answers

0
Vitruvie On

Yes; SwingUtilities.invokeLater() simply places your runnable on the AWT event queue to be processed later, and it is safe to do so at any time.

5
Hovercraft Full Of Eels On

JOptionPane is a Swing method and should be called on the EDT, the Event Dispatch Thread, and only on this thread, and so it suggests that all your code above should be on the EDT, and that most of your SwingUtilities.invokeLater(new Runnable() calls are completely unnecessary. The only necessary ones will be the main one, where you launch your Swing GUI code, and any areas where Swing calls need to be made from within background threads. Again, if any of the above code is being made within background threads, then the JOptionPane should not be in that thread.

For more specific information in this or any other answer, please provide more specific information in your question. Let's end all confusion. The best way to get us to fully and quickly understand your problem would be if you were to to create and post a minimal example program, a small but complete program that only has necessary code to demonstrate your problem, that we can copy, paste, compile and run without modification.

I have a sneaking suspicion that a decent refactoring along MVC lines could solve most of your problems. Your code is very linear with its lines of code that must follow one another and its if blocks, and it is also tightly coupled with your GUI, two red flags for me. Perhaps better would be less linear code, more event and state-driven code, code where your background code interacts with the GUI via observer notification, and where the background code likewise responds to state changes in the GUI from control notification.


Your control needs two SwingWorkers, one to get the row count and the other to get the rest of the data if the user decides to do so. I'd add a PropertyChangeListener to the first SwingWorker to be notified when the row count data is ready, and then once it is, present it to the view for the user to select whether or not to proceed. If he decides to proceed, I'd then call the 2nd SwingWorker to get the main body of the data.


For example, a rough sketch of what I'm talking about:

import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;

import javax.swing.*;

@SuppressWarnings("serial")
public class SwingWorkerFooView extends JPanel {
   private static final int PREF_W = 400;
   private static final int PREF_H = 300;
   private JProgressBar progressBar;
   private JDialog dialog;

   public SwingWorkerFooView() {
      add(new JButton(new ButtonAction("Foo", this)));
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   public boolean showOptionGetAllData(int numberOfRows) {
      String message = "Number of rows = " + numberOfRows + ". Get all of the data?";
      String title = "Get All Of Data?";
      int optionType = JOptionPane.YES_NO_OPTION;
      int result = JOptionPane.showConfirmDialog(this, message, title, optionType);

      return result == JOptionPane.YES_OPTION;
   }

   public void showProgressBarDialog() {
      progressBar = new JProgressBar();
      progressBar.setIndeterminate(true);
      Window window = SwingUtilities.getWindowAncestor(this);
      dialog = new JDialog(window, "Hang on", ModalityType.APPLICATION_MODAL);
      JPanel panel = new JPanel();
      panel.add(progressBar);
      dialog.add(panel);
      dialog.pack();
      dialog.setLocationRelativeTo(this);
      dialog.setVisible(true);
   }

   public void closeProgressBarDialog() {
      dialog.dispose();
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("SwingWorkerFoo");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new SwingWorkerFooView());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

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

@SuppressWarnings("serial")
class ButtonAction extends AbstractAction {
   Workers workers = new Workers();
   private SwingWorker<Integer, Void> firstWorker;
   private SwingWorker<List<String>, Void> secondWorker;
   private SwingWorkerFooView mainGui;

   public ButtonAction(String name, SwingWorkerFooView mainGui) {
      super(name);
      this.mainGui = mainGui;
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      firstWorker = workers.createFirstWorker();
      firstWorker.addPropertyChangeListener(new FirstPropertyChangeListener());
      firstWorker.execute();
      mainGui.showProgressBarDialog();
   }

   private class FirstPropertyChangeListener implements PropertyChangeListener {

      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
            mainGui.closeProgressBarDialog();
            try {
               int numberOfRows = firstWorker.get();
               boolean getAllData = mainGui.showOptionGetAllData(numberOfRows);
               if (getAllData) {
                  secondWorker = workers.createSecondWorker();
                  secondWorker.addPropertyChangeListener(new SecondPropertyChangeListener());
                  secondWorker.execute();
                  mainGui.showProgressBarDialog();
               } else {
                  // user decided not to get all data
                  workers.cleanUp();
               }
            } catch (InterruptedException e) {
               e.printStackTrace();
            } catch (ExecutionException e) {
               e.printStackTrace();
            }
         }
      }
   }

   private class SecondPropertyChangeListener implements PropertyChangeListener {
      @Override
      public void propertyChange(PropertyChangeEvent evt) {
         if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
            mainGui.closeProgressBarDialog();
            try {
               List<String> finalData = secondWorker.get();

               // display finalData in the GUI
            } catch (InterruptedException e) {
               e.printStackTrace();
            } catch (ExecutionException e) {
               e.printStackTrace();
            }
         }

      }
   }

}

class Workers {
   // database object that may be shared by two SwingWorkers
   private Object someDataBaseVariable;
   private Random random = new Random(); // just for simulation purposes

   private class FirstWorker extends SwingWorker<Integer, Void> {
      @Override
      protected Integer doInBackground() throws Exception {

         // The Thread.sleep(...) is not going to be in final production code
         // it's just to simulate a long running task
         Thread.sleep(4000);

         // here we create our database object and check how many rows there are
         int rows = random.nextInt(10 + 10); // this is just for demonstration purposes only

         // here we create any objects that must be shared by both SwingWorkers
         // and they will be saved in a field of Workers
         someDataBaseVariable = "Fubar";

         return rows;
      }
   }

   private class SecondWorker extends SwingWorker<List<String>, Void> {
      @Override
      protected List<String> doInBackground() throws Exception {

         // The Thread.sleep(...) is not going to be in final production code
         // it's just to simulate a long running task
         Thread.sleep(4000);

         List<String> myList = new ArrayList<>();
         // here we go through the database filling the myList collection

         return myList;
      }
   }

   public SwingWorker<Integer, Void> createFirstWorker() {
      return new FirstWorker();
   }

   public void cleanUp() {
      // TODO clean up any resources and database stuff that will not be used.
   }

   public SwingWorker<List<String>, Void> createSecondWorker() {
      return new SecondWorker();
   }
}

The key to all of this is to not to think in a linear console program way but rather to use observer design pattern, i.e., listeners of some sort to check for change of state of both the GUI and the model.

It's essentially:

  • create worker
  • add observer to worker (property change listener)
  • execute worker
  • show progress bar dialog or notify user in some way that worker is executing.

  • The listener will be notified when the worker is done, and then you can query the worker (here via the get() method call) as to its end result.

  • Then the progress dialog can be closed
  • And the view can display the result or get additional information from the user.