Java Threads

Discussion in 'Mac Programming' started by Wes, Oct 24, 2006.

  1. Wes macrumors 68020

    Wes

    Joined:
    Jun 22, 2001
    Location:
    London
    #1
    Hello all,

    I'm just finishing up a simple gui program and I'm having a small problem.

    The crux of my problem is this:

    I have two buttons (which call corresponding methods):
    Step: goes through a single iteration.
    Run: runs through steps until other GUI intervention stops it.

    Now, run has a while(true) situation and I want the run method to continue until the outside gui interaction. I have the action listeners and etc. all set up it's just when I click the run button I get 100% CPU and no GUI responsiveness. I'm assuming threads are the remedy to the problem but have little experience with them and would be grateful if somebody could suggest how the simple thread arrangement would be constructed.


    [I guess in other words I'd like one thread to manage the GUI and buttons and one for the computation].

    Many thanks for any help,

    Wes
     
  2. x704 macrumors regular

    x704

    Joined:
    Apr 15, 2006
    #2
    really the only way is to have a var like

    if (Stop == true){
    System.out.println("Opps, somebody must have pressed the stop button.");
    break;
    }

    in the method that is doing the CPU stuff.
    This is what I have in my run method:

    if (AbruptStop){
    break; // check to see if we pressed 'cancel'
    }

    if(Pause){
    try{
    BeforePauseTime = System.currentTimeMillis();

    wait(); //Pause the program, resume with notifyAll();

    AfterPauseTime = System.currentTimeMillis() - BeforePauseTime;
    TotalPauseTime += AfterPauseTime;
    } catch(InterruptedException e){}
    }

    then just change the vars to true with your button handeling method like this.

    MyButton4.addActionListener(new ActionListener(){
    // implement the cancel button here
    public void actionPerformed(ActionEvent e){
    if (HasThreadRun){
    if (!CalculatePrime.DoneCalculating()){
    if(!DidWeCancel){
    CalculatePrime.StopProcess();
    DidWeCancel = true;
    MyTextArea.append("Action canceled.\n");

    if(Pause)
    MyButton5.setText("Pause");

    Pause = false;
    }
    }
    }
    }
    });
     
  3. savar macrumors 68000

    savar

    Joined:
    Jun 6, 2003
    Location:
    District of Columbia
    #3
    Like the other guy said, changing while(true) to something like while(running) and make 'running' a boolean variable is probably a better idea. The GUI dispatches events on its own thread, so you're actually already running two threads. You just don't have a way for your thread to get the message.

    If you're interested in thread still, though, the easiest way is to write a new class that implements the Runnable interface. You write the code you want the thread to execute in a single method run().

    class MyThread implements Runnable {
    public void run() {
    while (true)
    // do stuff
    }
    }

    class MyTest {
    public static void main (String args[]) {
    (new Thread(new MyThread())).start();
    }
    }

    Google for it. If you can't figure it out post back.
     
  4. Wes thread starter macrumors 68020

    Wes

    Joined:
    Jun 22, 2001
    Location:
    London
    #4
    Okay guys,

    It works fine now but I have one small change to completely finish this.

    When it runs the updates are insanely fast so I need to tell the thread to sleep for a little bit in between each iteration. I've set up a Jslider to do this and monitor when this changes, but can't get sleep to use thread.sleep or whatever to achieve this. Any advice?

    Cheers,

    Wes
     
  5. Compile 'em all macrumors 601

    Compile 'em all

    Joined:
    Apr 6, 2005
    #5
    The method to use is sleep() which is static so you can just type in
    Thread.sleep(xxx) where xxx is time in ms. If you want your thread to sleep
    for 5 mins, you do Thread.sleep(5*60*1000).

    Remember that, after the sleep period is over your thread is not guaranteed
    to run straight away. This is particularly true if you have several threads
    running. when a thread wakes up its status will be changed to "runnable" and
    not "running".
     
  6. rtharper macrumors regular

    rtharper

    Joined:
    Sep 6, 2006
    Location:
    Oxford, UK
    #6

    You could also use a Semaphore or a Mutex to make sure that each iteration only starts and stops when the other work being done is finished. This is the only provably correct method (a semaphore/lock/mutex) to synchronize your threads. Calling sleep is still non-deterministic because the thread sleeps for x seconds only after it is active again the process queue, and as said before when it wakes it is not guaranteed to immediately run.

    It's a general rule of thumb for concurrent systems to never used hard-coded times. It's horribly bad practice.
     
  7. Wes thread starter macrumors 68020

    Wes

    Joined:
    Jun 22, 2001
    Location:
    London
    #7

    Works perfectly, thanks a lot!

    Point taken, but in a worst case scenario what could happen in my program? The GUI not update in sync with the operation? Or something more drastic?

    I do agree that in a much more larger program with less margin for error I would definitely go down the path of semaphores and etc.

    Thanks once again,

    Wes
     
  8. rtharper macrumors regular

    rtharper

    Joined:
    Sep 6, 2006
    Location:
    Oxford, UK
    #8
    In this case, probably. You could also get some erroneous values in your GUI because the computation could be swapped out mid-iteration and the GUI would try to update. The problem with concurrent threads is that you just don't really know; you can test it 100 times but the 101st could produce the error you're looking for.

    The java classes Semaphore and Lock will do pretty much what you need with almost no extra work at all.
     
  9. Compile 'em all macrumors 601

    Compile 'em all

    Joined:
    Apr 6, 2005
    #9
    If you are interested in synchronizing threads (i.e starting a thread after another finishes)
    then take a look at the join() method. For example,

    Thread t = new Thread();
    t.start();
    t.join();

    will result in the "current" thread starting after thread t finishes.
     
  10. Wes thread starter macrumors 68020

    Wes

    Joined:
    Jun 22, 2001
    Location:
    London
    #10
    I'd really like to do this with semaphores and after thinking about it I see how it work I have one concern. I need the iteration after a specified time in the GUI and I don't see how I can do this without the threads.
     
  11. MarkCollette macrumors 68000

    MarkCollette

    Joined:
    Mar 6, 2003
    Location:
    Calgary, Canada
    #11
    I mostly agree with everyone's advice, but have a few extra points.

    - Don't use Thread.sleep(). Instead use Object.wait() and Object.notify()

    - Make sure you're doing all of your calculations in the worker thread, whether the user clicked Run or Step.

    - If you have to update the GUI to show results, or do visualisations, then don't simply do it from the worker thread. The appropriate way is to package up the info into some object that implements Runnable, and pass that to javax.swing.SwingUtilities.invokeLater(Runnable). That will make it so that the GUI thread calls that object's run() method, at which time it can use the data it has to update your GUI.

    - Recognise that there's a flow of execution:

    1. The user clicks, and that's processed in the GUI thread.
    2. An ActionListener sets a variable, and notifies the worker thread.
    3. The worker thread wakes up, checks that field, and postentially does some work.
    4. When the work is completed, the results are bundled up and invokedLater.
    5. The GUI thread wakes up and executes the Runnable.

    At the point of execution of the Runnable, updating the GUI may or may not still be applicable, depending on which user interactions have happenned since step 1. For example, the user may have close the window, or clicked Cancel, or switched to a different visualisation view, or whatever. Just be aware at each hand-off point, that the situation might have already changed. That's why it's important for there to be one single point where state is maintained, that supercedes all others. Let's look at it again:

    1. The user clicks, and that's processed in the GUI thread.
    2. An ActionListener sets a variable, and notifies the worker thread.
    // In a synchronised block
    GlobalState = RUNNING
    3. The worker thread wakes up, checks that field, and postentially does some work.
    // In a synchronised block
    WorkerThread.LocalState = GlobalState
    4. When the work is completed, the results are bundled up and invokedLater.
    // If there's heavy processing, might want to see if should bother
    // bundling results. But even then, you can have situations where
    // LocalState == STEP and GlobalState == STOP, but we still want
    // to see the results of stepping
    5. The GUI thread wakes up and executes the Runnable.
    // Definitely have to check GlobalState to see if should use results
    // since could have changed modes or whatever. Remember, it
    // was an optimisation to check at step 4, but it's imperative here.

    So, notice how we actually store state in two places, but the GlobalState has precedence? And LocalState is really just a temporary working state for the worker thread.
     
  12. rtharper macrumors regular

    rtharper

    Joined:
    Sep 6, 2006
    Location:
    Oxford, UK
    #12
    Careful with notify, it can have a non-deterministic behavior in multi-threaded environments. However, those are essentially analogous to using Semaphores in terms of behvaior, and semaphores tend to be a more elegant solution, imho.
     
  13. MarkCollette macrumors 68000

    MarkCollette

    Joined:
    Mar 6, 2003
    Location:
    Calgary, Canada
    #13
    Do you mean using Object.notify() versus Object.notifyAll() ?
    I usually use notifyAll(). I think that notify() is usually only used as an optimisation, when you really only want one Thread to wake up, and don't care which.
     
  14. rtharper macrumors regular

    rtharper

    Joined:
    Sep 6, 2006
    Location:
    Oxford, UK
    #14
    Yeah, that's what I was saying =) Probably the most evil part of concurrent programming in Java is that you either a) wake up a random thread or b) you have to wake up all the threads. Imagine if OS thread/process libraries were written that way...

    I have to do a lot of message passing parallel stuff in Java right now. After looking at other options that have been used, I have to say the Java thread library is one of the most horribly conceived ideas ever.
     

Share This Page