Click to See Complete Forum and Search --> : A Simple Threading Problem
idavidcrockett
March 22nd, 2007, 09:41 PM
Hi CodeGuru.com,
My question pertains to threading. I am new to the topic, but I have read the tutorials available from Sun and others.
Anyways my question comes down to this simple code:
public class Main {
static void ThreadMessage(String input) {
// Display a given message
System.out.println(input);
// Wait 10 seconds
long tic=System.currentTimeMillis();
while (System.currentTimeMillis()-tic < 10000) {
}
// Display a termination message
System.out.println("finished");
}
public static void main(String[] args) {
// Display Ahoy
ThreadMessage("Ahoy");
// Display Arr
ThreadMessage("Arr");
}
}
The output goes like this:
Ahoy
finished
Arr
finished
BUILD SUCCESSFUL (total time: 21 seconds)
I want to use all the cool tricks associated with threading to make the output look like this:
Ahoy
Arr
finished
finished
BUILD SUCCESSFUL (total time: 11 seconds)
Any ideas?
gmrowe1075
March 22nd, 2007, 10:02 PM
The problem is you're only running a single thread. There are no multiple threads here. To execute a separate thread in Java you must either create a Runnable that you pass to the Thread class, and then call the Thread.start() method, or extend Thread directly and override the Thread.run() method and call Thread.start().
HighCommander4
March 22nd, 2007, 10:13 PM
I can show you the code how to do it. If you want to understand it, you need to be familiar with concepts like interfaces and inheritance.
Assuming that you're familiar with these concepts, here's the explanation:
To make code run in a separate thread, you need to write a class that implements the Runnable interface and defines its run() method. The code that the thread will execute goes into the run() method. To start the thread, you create a Thread object and pass to its constructor an instance of the class you wrote (the one that extends Runnable). Then, you can start executing the code by calling start() on the thread object, or interrupt execution of the code by calling interrupt().
That said, here is your code modified so that each time ThreadMessage() is called, it creates a new thread to execute the code. Note that in my code, I called the class that implements Runnable "MyRunnable".
public class Main
{
static void ThreadMessage (String input)
{
Thread thread = new Thread (new MyRunnable (input));
thread.start ();
}
static class MyRunnable implements Runnable
{
String input;
public MyRunnable (String input)
{
this.input = input;
}
public void run ()
{
// Display a given message
System.out.println (input);
// Wait 10 seconds
long tic = System.currentTimeMillis ();
while (System.currentTimeMillis () - tic < 10000)
{
}
// Display a termination message
System.out.println ("finished");
}
}
public static void main (String[] args)
{
// Display Ahoy
ThreadMessage ("Ahoy");
// Display Arr
ThreadMessage ("Arr");
}
}
p.s. I just saw this is your first post. Welcome to CodeGuru!
HighCommander4
March 22nd, 2007, 10:29 PM
I just wanted to add (this is not relevant to your question but it's good advice in general) that using an empty while() loop and testing if a certain period of time has passed by is a poor way of "delaying". The computer keeps checking the condition repeatedly until the 10 seconds is up, which clogs up the CPU. Call up the task manager while your program is running and see how much CPU your program uses: when I ran it it was in the high 90%-s (which is very bad because it basically slows down every other operation on your computer to a crawl).
A better way to "delay" is to use the Thread.sleep() function and pass to it the amount of time in milliseconds you want to delay. This effectively "pauses" the thread for the duration of the delay, during which time its CPU usage is very minimal.
Note that the Thread.sleep() function throws InterruptedException, so you have to enclose the call in a try block and catch InterruptedException (declaring the enclosing function as throwing InterruptedException is not an option because the call is made from the run() method which is defined in the Runnable class and which is not allowed to throw InterruptedException). Since trying and catching every time you need to delay is bothersome, it's better to encapsulate this functionality in its own method, which I called delay():
public class Main
{
static void ThreadMessage (String input)
{
Thread thread = new Thread (new MyRunnable (input));
thread.start ();
}
static class MyRunnable implements Runnable
{
String input;
public MyRunnable (String input)
{
this.input = input;
}
public void run ()
{
// Display a given message
System.out.println (input);
// Wait 10 seconds
delay (10000);
// Display a termination message
System.out.println ("finished");
}
}
static void delay (int ms)
{
try
{
Thread.sleep (ms);
}
catch (InterruptedException e)
{
System.err.println ("Thread interrupted!");
}
}
public static void main (String[] args)
{
// Display Ahoy
ThreadMessage ("Ahoy");
// Display Arr
ThreadMessage ("Arr");
}
}
idavidcrockett
March 22nd, 2007, 10:51 PM
Thanks for the code help HighCommander,
Threads are really freaking cool. I looked into inheritance, and interface, but those concepts seemed really abstract and hard to understand.
I have one more question. Say I do not want the code to say Arr until I know, that it has said Ahoy. Any ideas? Should the notify functions be used?
HighCommander4
March 23rd, 2007, 12:23 PM
Threads are really freaking cool. I looked into inheritance, and interface, but those concepts seemed really abstract and hard to understand.
Don't worry about it. I've been programming for a number of years yet still I frequently have the experience where I'm just starting to understand a piece of code that I've been using for years. In my personal opinion, it doesn't make sense to have to understand everything before you can do anything.
I have one more question. Say I do not want the code to say Arr until I know, that it has said Ahoy. Any ideas? Should the notify functions be used?
That's an interesting requirement. Could you explain a little more? For example, do you want threads to start displaying their first message only when all threads have been started? Or you just want to make sure the threads do their reporting in the order in which they were started?
idavidcrockett
March 23rd, 2007, 01:15 PM
Hey HighCommander,
Yes my question was really vague. I'll clarify.
When I run this code:
public class Main
{
static void ThreadMessage (String input)
{
Thread thread = new Thread (new MyRunnable (input));
thread.start ();
}
static class MyRunnable implements Runnable
{
String input;
public MyRunnable (String input)
{
this.input = input;
}
public void run ()
{
// Display starting message
System.out.println (input+" is starting");
// Wait 5 seconds
delay (5000);
// Display halfway point
System.out.println(input+" is halfway");
// Now start the other thread, but how?
// Wait another 5 seconds
delay (5000);
// Display a termination message
System.out.println (input+" is finished");
}
}
static void delay (int ms)
{
try
{
Thread.sleep (ms);
}
catch (InterruptedException e)
{
System.err.println ("Thread interrupted!");
}
}
public static void main (String[] args)
{
// Display Ahoy
ThreadMessage ("Wayne");
// Display Arr
ThreadMessage ("Charlie");
}
}
The output is:
Wayne is starting
Charlie is starting
Wayne is halfway
Charlie is halfway
Charlie is finished
Wayne is finished
BUILD SUCCESSFUL (total time: 11 seconds)
But I want the output to be:
Wayne is starting
Wayne is halfway
Charlie is starting
Wayne is finished
Charlie is halfway
Charlie is finished
BUILD SUCCESSFUL (total time: 16 seconds)
OR
Wayne is starting
Wayne is halfway
Charlie is starting
Charlie is halfway
Wayne is finished
Charlie is finished
BUILD SUCCESSFUL (total time: 16 seconds)
I do not want to start the other thread until the first one has ran for 5 seconds. Is this possible?
BTW, I am using the answers to these questions to optimize a single stranded webcrawler I wrote. When I first wrote the web crawler it took 10minutes to perform a compact algorithm on 100URLs. After introducing multiple threads, the crawler takes about 2minutes. It needs a lot of work.
Thanks,
DC
keang
March 23rd, 2007, 01:46 PM
You need to be careful when talking about print output order when dealing with multi-threaded systems as there are no guarantees as to the run order or amount of time a thread will run for. If you run your program many times you will undoubtedly see the print out order change.
If you need to guarantee the order threads are running in you need to use some form of synchronisation such as one of the classes in the java.util.concurrent package eg CyclicBarrier.
HighCommander4
March 23rd, 2007, 05:43 PM
I do not want to start the other thread until the first one has ran for 5 seconds. Is this possible?
Well, when you start your threads like this:
// Display Ahoy
ThreadMessage ("Wayne");
// Display Arr
ThreadMessage ("Charlie");
These two statements are executed by the main thread one right after the other. If you want to start the second thread only after the first one has ran for 5 seconds, you need to add the delay here:
// Display Ahoy
ThreadMessage ("Wayne");
delay(5000);
// Display Arr
ThreadMessage ("Charlie");
Of course this might not be an acceptable solution in all cases. For example, the second thread may be waiting for the first thread to complete an operation rather than waiting for a set amount of time. In this case, you may want the first thread to set a flag (i.e. boolean variable) when it is ready for the second thread to start. In main(), then, after the first thread is started, you wait for the flag to be set before starting the second thread.
Note that in waiting for the flag to be set, you have to use a while loop, but to conserve CPU cycles you can put a small-interval delay within the loop, as such:
// Display Ahoy
ThreadMessage ("Wayne");
while (flagIndicatingSecondThreadCanStart == false)
delay(5);
// Display Arr
ThreadMessage ("Charlie");
This way, the second thread will start at most five milliseconds after the first thread signals that it is ready for the second thread to start, which is acceptable in most cases.
keang
March 24th, 2007, 07:50 AM
These two statements are executed by the main thread one right after the other.Not necessarily. The main thread may create both threads before either of them run or it may create the first thread (Wayne) and then be task switched out whilst the wayne thread runs for a time and then once the task switcher hands the execution context back to the main thread it will create the second thread (Charlie).
Be really careful when dealing with multiple threads, the order of execution cannot be determined unless you use some form of thread safe synchronisation.
Note that in waiting for the flag to be set, you have to use a while loop, but to conserve CPU cycles you can put a small-interval delay within the loop, as such:
Yes this will work but it's not an ideal solution. For a start it's not scaleable, for every new background thread you want to run you need to have a new flag. It would be better to put the flag inside the background thread task, or better still provide a notification/listener mechanism so that the background task notifies any registered objects that it has completed it's first stage of execution. By using this type of system you can create as many background threads as you like and register each new thread as a listener to the preceeding thread and just let them run. When a thread has reached the required point of execution it notifies the next thread to start and so on until all threads complete.
TheCPUWizard
March 24th, 2007, 10:01 AM
Emphasis added....
the second thread will start at most five milliseconds after the first thread signals
Actually that should read "at least". The minimum delay will be 5mS but could be any time (10mS, 100mS, 1S, or longer) before the second thread is started.
keang
March 24th, 2007, 01:30 PM
the second thread will start at most five milliseconds after the first thread signals
Actually that should read "at least". The minimum delay will be 5mS but could be any time (10mS, 100mS, 1S, or longer) before the second thread is started. Actually both statements are wrong. If the flag is set just before the loop is entered there will, in the best case, be no wait (other than the time taken to execute the if statement and create the next thread) whereas if the flag is set as the delay is started there will, in the worst case, be a 5 milliseconds delay plus the time taken by running other threads before this thread is given the execution context. So the delay could be anything at all.
HighCommander4
March 24th, 2007, 02:44 PM
Not necessarily. The main thread may create both threads before either of them run or it may create the first thread (Wayne) and then be task switched out whilst the wayne thread runs for a time and then once the task switcher hands the execution context back to the main thread it will create the second thread (Charlie).
You're right, it won't be literally "one right after the other". I just wanted to point out that nothing along the lines of the main thread waiting for Wayne or Charlie to finish or to accomplish something will happen unless code is explicitly written to do that.
In any case, I like to think of threads as essentially running at the same time because if you think of the sum total of all the threads being executed (for instance the Event Dispatch Thread), then no two consecutive statements are necessarily executed "one right after the other" in a literal sense.
there will, in the worst case, be a 5 milliseconds delay plus the time taken by running other threads before this thread is given the execution context. So the delay could be anything at all.
I made the assumption that "the time taken by running other threads before this thread is given the execution context" is neglibile in comparison to 5 milliseconds. But you're right, this assumption isn't necessarily true in all case.
codeguru.com
Copyright 2007 Jupitermedia Corporation All Rights Reserved.