Solved

Need help with muti thread gui

Posted on 2004-11-01
213 Views
Last Modified: 2010-03-31
Hi all,

I have a Gui which launches a simulation which runs in its own thread.  While the simulation is running, it displays a Gui showing its progress.  My problem is that because the simulation is extremely processor intensive the Gui displaying its progress takes ages to show itself.  

Here is the code, can anyone tell me any structural mistake I am making?

// Here is how it is called from the main GUI:

      Simulation simulation = new Simulation(simulationEcology);
      simulation.start();

// And here is class Simulation:

public class Simulation extends Thread implements ActionListener {

  public Simulation(Ecology ecology) {
    this.ecology = ecology;
    createAndShowGUI(); // creates a GUI displaying the simulation's progress.  
  }

  public void run() {    
    runSimulation();  // Very processor intensive
  }
0
Question by:Dawkins
    32 Comments
     
    LVL 86

    Expert Comment

    by:CEHJ
    You need to ensure that any updates to the gui are made in the gui thread. In your case, in your run, you could do this by doing EventQueue.invokeAndWait, with a Runnable that updates the gui
    0
     

    Expert Comment

    by:krispin
    I would imagine that if the method  "runSimulation();" is processor intensive there may be no way around it other than trying to make you method more efficient. Try remove a few variables (and re-using others, etc). Without seeing the code for the method I'd be unsure of what else to suggest
    0
     
    LVL 86

    Expert Comment

    by:CEHJ
    >>in your run

    Should really have said 'in your runSimulation'
    0
     

    Author Comment

    by:Dawkins
    It isn't really *updates* to the Gui that are the problem - it's mainly just the fact that the Gui takes ages to *create* in the first place.  

    By setting the simulation thread's priority to lowest it cures the problem - but I want it to have as much processor as possible so that it finishes as quickly as possible.

    So maybe the solution is some sort of hack which sets the priority to lowest to allow the Gui to form, then after an arbitrary amount of time it increases the priority?  This doesn't seem like a good design to me though(?).
    0
     
    LVL 86

    Expert Comment

    by:CEHJ
    Are you sure that the gui has been allowed to fully show itself in its initial state before the simulation thread has been started?
    0
     

    Author Comment

    by:Dawkins
    > Are you sure that the gui has been allowed to fully show itself in its initial state before the simulation thread has been started?

    Nope that sounds like exactly the problem.  How do I check this or allow for it?
    0
     
    LVL 4

    Expert Comment

    by:lcwiding
    Without seeing the code for your main thread, it is hard to be more specific. But, one issue to consider is that Java uses a cooperative threading model, so other threads will not get time unless a thread gives the a chance. If your simulation thread is computational intense, I would recommend interspersing some calls to Thread.yield() within your loop to provide opportunities for other threads to jump in and run.

    If it is just the initial display that you want to make sure appears quickly, you could try setting up either an initial sleep call before starting your main thread so that the UI has time to initialize, or set a flag that your UI code will set once it has initialized. Your simulaiton thread could set a wait on that flag, which the UI flag could then issue a notify to reawaken the simulation thread.
    0
     

    Author Comment

    by:Dawkins
    > or set a flag that your UI code will set once it has initialized

    I tried this by changing a flag after the method createAndShowGui had finished, but it didn't work.  Even though my code had finished executing, the gui still hadn't appeared on screen.  My novice speculation is that although my setup code had finished, a separate thread actually handles the displaying of the graphics on the screen, and this was having trouble getting enough processor time?

    If it helps, here is the main method:

      public static void main(String arguments[]) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            createAndShowGUI();
          }
        });
      }

    The Gui created by createAndShowGUI() is the one which then creates the simulation thread.
    0
     

    Author Comment

    by:Dawkins
    Another point I should make - the reason this is such a problem is that I am setting the priority for the processor intensive thread to Thread.MAX_PRIORITY -1

    I'm doing this because I want the simulation to run as fast as possible.. but maybe it isn't such a good idea..
    0
     
    LVL 4

    Assisted Solution

    by:lcwiding
    As part of setting up the GUI, have you called repaint() on the container your UI is displayed in? After doing so, I would recommend a quick call the Thread.sleep(n), where n is 500 to 1000 to provide the system some time to perform the actual repainting.

    Finally, I would also recommend that you place the Thread.yield() calls into your processing thread, so that the UI will get the occassional time slices needed for painting, and this includes for situations where a user might switch to another application, and so your app would need to repaint its window.
    0
     
    LVL 86

    Expert Comment

    by:CEHJ
    You should create the gui on the main application thread

    >>is that I am setting the priority for the processor intensive thread to Thread.MAX_PRIORITY -1

    That's probably not such a good idea *at this point*.
    0
     

    Author Comment

    by:Dawkins
    ok I now create the GUI from within the simulation thread and put a sleep in as you recommended and it seems to work:

      public void run() {    
        createAndShowGUI();
        try {
          Thread.sleep(100);
        }
        catch (InterruptedException ex) {
        }
        runSimulation();
      }

    I've also put Thread.yield() into the simulation loop :

    private void runSimulation() {
        startTime = System.currentTimeMillis();
        while (ecology.getCurrentGeneration() < ecology.getNumberOfGenerations() && !isStopped && !isCancelled) {
        ....................... lots of calculations
        Thread.yield() ;
        }
    }
    0
     

    Author Comment

    by:Dawkins
    > You should create the gui on the main application thread

    I did have it like that but changed it after reading the tutorial where it says to put it into an invokelater when using the latest SDK.  I can't find the exact quote but this alludes to it:

    http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html

    "You have probably noticed that most of the tutorial's demos use a standardized main method that calls the SwingUtilities method invokeLater to ensure that the GUI is created on the event-dispatching thread. "
    0
     
    LVL 86

    Expert Comment

    by:CEHJ
    >>.  I can't find the exact quote but this alludes to it

    You're right - this is what they're saying:

    "To avoid the possibility of thread problems, we recommend that you use invokeLater to create the GUI on the event-dispatching thread for all new applications"

    That's a great pity, and another nail in the coffin for client-side Java
    0
     
    LVL 4

    Expert Comment

    by:lcwiding
    >> invokeLater

    This is, IMHO, another reason to avoid Swing. But that is another discussion.

    One thought on when you were using a flag to indicate that the UI had been generated, how did you implement that? My recommendation would be something like:

      public Object uiWait = new Object();

      public static void main(String arguments[]) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            createAndShowGUI();
            uiWait.notifyAll();
          }
        });

      public void run() {    
        try {
          uiWait.wait();
          Thread.sleep(100);
        }
        catch (InterruptedException ex) {
        }
        runSimulation();
      }

    0
     
    LVL 92

    Expert Comment

    by:objects
    Use of the SwingWorker class mentioned in the above link is useful for running code in a seperate thread.

    0
     
    LVL 92

    Accepted Solution

    by:
    From a design perspective though, the implementation of your simulation and your gui should be seperate and thus minimising the types of problems you are running into. So your simulation should not worry about updating the gui, and instead the gui updates according to the current state of the sim.
    0
     

    Author Comment

    by:Dawkins
    ok thanks for the help so far.  I've increased the points because I want to get into this in more detail!

    -----
    >>From a design perspective though, the implementation of your simulation and your gui should be seperate and thus minimising the types of problems you are running into. So your simulation should not worry about updating the gui, and instead the gui updates according to the current state of the sim.
    -----
     
    Hmm I don't understand how to do this.   My code for the simulation loop is below.  It updates the gui every iteration of the simulation loop.  It also can be paused via the Gui and have it's thread priority changed by the gui.  To do what you are suggesting wouldn't I need a separate loop running in the Gui thread to check for values set by the simulation thread?

    private void runSimulation() {
        startTime = System.currentTimeMillis();
        while (ecology.getCurrentGeneration() < ecology.getNumberOfGenerations() && !isStopped && !isCancelled) {
          if( (this.getPriority() != threadPriority) ){
            this.setPriority(threadPriority);
          }

          ecology.playGeneration();
          progressBar.setValue(ecology.getCurrentGeneration());
          progressBar.setString("Generation: "+ecology.getCurrentGeneration()+" / "+ecology.getNumberOfGenerations());
          lastCheckedTime = System.currentTimeMillis();
          long timeTakenToDoChunk = lastCheckedTime - startTime;
          float progress = (float)ecology.getNumberOfGenerations() / (float)ecology.getCurrentGeneration();
          long estimatedTotalTime = Math.round(progress * timeTakenToDoChunk);

          timeRemainingLabel.setText(TIME_REMAINING_TXT +
                                     millisecondsToTime(estimatedTotalTime - timeTakenToDoChunk));
          while (isPaused) {
            synchronized (this) {
              try {
                wait();
              }
              catch (InterruptedException ex) {
              }
            }
          }
          Thread.yield();
        }
        this.setPriority(NORM_PRIORITY);
        if(isCancelled != true) {
          ecology.setNumberOfGenerations(ecology.getCurrentGeneration());
          Sounds.playEnd();
          ViewResultsFrame resultsGUI = new ViewResultsFrame(ecology);
          resultsGUI.show();
        }
        frame.dispose();
      }
    0
     
    LVL 92

    Expert Comment

    by:objects
    Define a listener that includes methods required to notify of events of interest during the simulation.
    For example something like:

    public interface SimulationListener
    {
       public void progress(Simulation sim, Ecology eco, long timeTaken, long totalTime);
       public void paused(Simulation sim, Ecology eco);
       public void resumed(Simulation sim, Ecology eco);
       public void loop(Simulation sim, Ecology eco);
    }

    The your sim class would then call the listener(s), instead of updating the gui.
    And you would have a listener that would handle updating the gui, removing any coupling between the sim and the gui.
    0
     
    LVL 86

    Expert Comment

    by:CEHJ
    Don't forget to place any signal to the gui, having received any events on that listener, on the event dispatch thread
    0
     

    Author Comment

    by:Dawkins
    Ok, before I try this could you just check I'm on the right track?  Am I right in thinking I would have a new class like this:

    Class SimulationGUI implements SimulationListener

    And then pass a reference to the SimulationListener interface into the Simulation class like this:

    Class Simulation extends Thread {
      public Simulation(SimulationListener gui) {}
    public void run() {
    }
    }


    0
     

    Author Comment

    by:Dawkins
    (Didn't mean to submit... )

    Then every loop in the Simulation I put an event on the event dispatch thread like this:

        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                gui.progress(Simulation sim, Ecology eco, long timeTaken, long totalTime);
            }
        });

    0
     
    LVL 86

    Expert Comment

    by:CEHJ
    The Simulation class should have an addListener method, just as with guis and addActionListener
    0
     
    LVL 86

    Expert Comment

    by:CEHJ
    invokeLater would be used to do the actual update to a gui
    0
     

    Author Comment

    by:Dawkins
    >  The Simulation class should have an addListener method, just as with guis and addActionListener

    Surely the simulation *gui* should have the listener shouldn't it?  Because it's listening for changes in the simulation loop?

    I'm getting more confused here :).   Would someone be able to just give a 1-2-3 set of steps for separating the Gui out of the simulation?  I'm not even sure of step 1:

    1)  Create a new class SimulationGUI.  Put all gui creation stuff in here (including event handlers for the pause button? Or do I put the button event handlers into the Simulation class?)
    0
     
    LVL 86

    Assisted Solution

    by:CEHJ
    >>Surely the simulation *gui* should have the listener shouldn't it?  

    Yes, but the Simulation has the addListener method. Think of it as adding an actionListener to a button. The gui has the listener with actionPerformed, but the button itself has the list of listeners which is added to by addActionListener

    1) should be one of the last steps. You need to do the listening stuff first
    0
     
    LVL 92

    Expert Comment

    by:objects
    > Surely the simulation *gui* should have the listener shouldn't it?

    no it *is* the listener, ie. your gui (or whatever class is used to control it) implements SimulationListener

    > Would someone be able to just give a 1-2-3 set of steps for separating the Gui out of the simulation?

    Create a Listener interface for your sim class, adding methods to add/remove listener (for simplicity you could initially just support a single listner). Also add code to fire the relevant event when they occur.

    Create a listener implementation that updates the gui appropriately accordingly.
    0
     

    Author Comment

    by:Dawkins
    ok I'm trying to implement your suggestions.  I still have a major question about how the gui is going to send events *back* the other way to the Simulation (to pause it etc).  But anyway,  here's what I've done so far could you tell me if this is what you meant?:

    1) There's now 3 classes to replace the single original class Simulation:

    --------code-----------
    Class Simulation extends Thread

    Class SimulationFrame extends JFrame  implements SimulationListener, ActionListener, ChangeListener, ItemListener

    Interface SimulationListener
    --------/code---------

    2) I've added a the SimulationListener to the Simulation like this:

    --------code-----------
    Class Simulation extends Thread{
      Ecology ecology;
      SimulationListener simListener;

      public Simulation(Ecology ecology, SimulationListener listener) {
        this.ecology = ecology;
        this.simListener = listener;
      }
    --------/code---------

    3) Then I send events from within the run() method like this:

    --------code-----------
      public void run() {
        while(.....) {
          ........... // run iteration
          final long timeLeft = estimatedTotalTime - timeTakenToDoChunk;
          javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                simListener.progress(timeLeft);
            }
         }
    --------/code---------
    0
     

    Author Comment

    by:Dawkins
    Well the above seems to work even if I have got the wrong end of the stick with the design!

    But it only works one way.  How do I get the Gui to send information back to the simulation?  

    My current guess is to do the reverse of the above:  make a new interface called "SimulationGuiListener which the Simulation can implement with methods such as "pause()" and "changeThreadPriority()" and communicate via that.  

    Can anyone confirm this as being stupid?
    0
     
    LVL 92

    Expert Comment

    by:objects
    >  I still have a major question about how the gui is going to send events *back* the other way to the Simulation (to pause it etc).

    You would simply call the Sum method that performs the required action
    eg. if you want to pause call it's pause() method (or whatever it may be called)

    no listener interface required here


    0
     
    LVL 86

    Expert Comment

    by:CEHJ
    8-)
    0
     
    LVL 92

    Expert Comment

    by:objects
    (:
    0

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    What Should I Do With This Threat Intelligence?

    Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

    Suggested Solutions

    Java Flight Recorder and Java Mission Control together create a complete tool chain to continuously collect low level and detailed runtime information enabling after-the-fact incident analysis. Java Flight Recorder is a profiling and event collectio…
    Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
    Viewers will learn about the regular for loop in Java and how to use it. Definition: Break the for loop down into 3 parts: Syntax when using for loops: Example using a for loop:
    The viewer will learn how to implement Singleton Design Pattern in Java.

    857 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    18 Experts available now in Live!

    Get 1:1 Help Now