Friday, February 25, 2011

Chapter 56: Threads and Multithreading

This chapter and the subsequent ones on Threads and Multi-threading is probably by far the most complex topic we will be covering as part of our aim at getting a SCJP Certification.
Before we begin – let me put out a Disclaimer…

Disclaimer: This chapter makes almost no attempt to teach you how to design a good, safe, multithreaded application. The concepts covered in this article are just the tip of the iceberg and you’re here only to learn the basics of threading, and what you need to get through the thread questions on the exam. Before you can write decent multithreaded code, however, you really need to study more on the complexities and subtleties of multithreaded code.

In a single-threaded runtime environment, actions execute one after another. The next action can happen only when the previous one is finished. If task A takes half an hour, and the user wants to do something else, he is doomed to wait until task A is over.

Ideally, the user should be able to carry on with his other task until his first task is complete. Imagine having to wait till your eclipse build all your source code before you could check your email? Thankfully windows uses multiple threads to run your eclipse and outlook. So you don't have to go through this pain. Similarly java has options wherein the programmer can have multiple threads of execution running.

Now you are ready to dive into the magical or rather complicated world of threads.

Lets get started!!!

What is a Thread?

In Java, “thread” means:
• A thread of execution

A thread of execution is an individual process that has its own call stack. In Java, there is one thread per call stack or, to think of it in reverse, one call stack per thread. Even if you don’t create any new threads in your program, threads are there running without your knowledge.
If you are curious and ask me how? Just stop for a moment and think how you would execute a java program? You’ll write a main method and then execute it from the command line using the “java” command. Well, you have already answered the question because, when your main method starts, the JVM assigns a thread for this main method and assigns it a call stack. So, eventhough you did not intentionally create a thread, the system did one for you.

The most important concept to understand from this entire chapter is this:

When it comes to threads, very little is guaranteed.

So be very cautious about interpreting the behavior you see on one machine as “the way threads work.” The exam expects you to know what is and is not guaranteed behavior, so that you can design your program in such a way that it will work regardless of the underlying JVM.

Exam Tip:
The thread questions are among the most difficult questions on the exam. In fact, for most people they are the toughest questions on the exam. If you’re not already familiar with threads, you’ll probably need to spend some time experimenting.
Exam Tip:
The topic of daemon threads is NOT on the exam. All of the threads discussed in this chapter are “user” created threads. We can create a second kind of thread called a daemon thread. The difference between these two types of threads (user and daemon) is that the JVM exits an application only when all user threads are complete—the JVM doesn’t care about letting daemon threads complete, so once all user threads are complete, the JVM will shut down, regardless of the state of any daemon threads. Once again, this topic is NOT on the exam and it is enough if you know this much about daemon threads for the exam.

Creating a Thread

A thread in Java begins as an instance of java.lang.Thread. You’ll find methods in the Thread class for managing threads including creating, starting, and pausing them. They are:
start()
yield()
sleep()
run()

All the awesome action happens in the run() method. Think of the code you want to execute in a separate thread as the job to do. Lets say, you have some work that needs to be done, in the background while other things are happening in the program, so what you really want is that work to be executed in its own thread. All that code you want executed in a separate thread goes into the run() method.

Ex:
public void run() {
// your job code goes here
}

The run() method will call other methods, of course, but the thread of execution—the new call stack—always begins by invoking run(). So where does the run() method go? In one of the two classes you can use to define your thread job.

You can define and instantiate a thread in one of two ways:
• Extend the java.lang.Thread class.
• Implement the Runnable interface.

You need to know about both for the exam, although I would personally recommend that you implement Runnable than extend Thread. Extending the Thread class is the easiest, but it’s usually not a good OO practice.

Now, you may ask me “Why? Why should I choose the Runnable over Thread?”
Well, if you thought of the why before reading the preceding line, give yourself a pat on the back. Well done!

Because, if you extend the Thread class what would you do if you have to extend another concrete parent class that has vital/important behavior that you need in your class? Get the point? Java does not support direct multiple inheritance and hence, once you extend the Thread class, you are done, for good. You cannot extend any other class. That is why I recommended the Runnable interface. Even if you implement the interface, you are free to extend any class you want. Convenient, right?

Defining a Thread

To define a thread, you need a place to put your run() method, and as we just discussed, you can do that by extending the Thread class or by implementing the Runnable interface. We’ll look at both in this section.

Extending java.lang.Thread
The simplest way to define code to run in a separate thread is to
• Extend the java.lang.Thread class.
• Override the run() method.
It looks like this:
class MyFirstThread extends Thread {
public void run() {
System.out.println("Important job running in MyFirstThread");
}
}
The limitation with this approach is that if you extend Thread, you can’t extend anything else. And it’s not as if you really need that inherited Thread class behavior, because in order to use a thread you’ll need to instantiate one anyway.

Keep in mind that you’re free to overload the run() method in your Thread subclass:
class MyFirstThread extends Thread {
public void run() {
System.out.println("Important job running in MyFirstThread");
}
public void run(String s) {
System.out.println("String running is " + s);
}
}

Note: The overloaded run(String s) method will be ignored by the Thread class unless you call it yourself. The Thread class expects a run() method with no arguments, and it will execute this method for you in a separate call stack after the thread has been started. With a run(String s) method, the Thread class won’t call the method for you, and even if you call the method directly yourself, execution won’t happen in a new thread of execution with a separate call stack. It will just happen in the same call stack as the code that you made the call from, just like any other normal method call.


Implementing java.lang.Runnable

Implementing the Runnable interface gives you a way to extend any class you like, but still define behavior that will be run by a separate thread. It looks like this:

class MyFirstRunnableClass implements Runnable {
public void run() {
System.out.println("Imp job running in MyFirstRunnableClass");
}
}
Regardless of which mechanism you choose, you’ve now got yourself some code that can be run by a thread of execution. So now let’s take a look at instantiating your thread-capable class, and then we’ll figure out how to actually get the thing running.

Instantiating a Thread

Remember, every thread of execution begins as an instance of class Thread. Regardless of whether your run() method is in a Thread subclass or a Runnable implementation class, you still need a Thread object to do the work.

If you extended the Thread class, instantiation is dead simple:
MyFirstThread t = new MyFirstThread()

If you implement Runnable, instantiation is only slightly less simple. To have code run by a separate thread, you still need a Thread instance. But rather than combining both the thread and the run() method into one class, you’ve split it into two classes—the Thread class for the thread-specific code and your Runnable implementation class for your job-that-should-be-run-by-a-thread code.

First, you instantiate your Runnable class:
MyFirstRunnableClass r = new MyFirstRunnableClass();

Next, you get yourself an instance of java.lang. Thread, and you give it your job!
Thread t = new Thread(r); // Pass your Runnable to the Thread

If you create a thread using the no-arg constructor, the thread will call its own run() method when it’s time to start working. That’s exactly what you want when you extend Thread, but when you use Runnable, you need to tell the new thread to use your run() method rather than its own. The Runnable you pass to the Thread constructor is called the target or the target Runnable.
You can pass a single Runnable instance to multiple Thread objects, so that the same Runnable becomes the target of multiple threads, as follows:

public class TestMyThreads {
public static void main (String [] args) {
MyFirstRunnableClass r = new MyFirstRunnableClass();
Thread aaa = new Thread(r);
Thread bbb = new Thread(r);
Thread ccc = new Thread(r);
}
}

Giving the same target to multiple threads means that several threads of execution will be running the very same job and that same job will be done multiple times.

Exam Tip:
The Thread class itself implements Runnable. (After all, it has a run() method that we were overriding.) This means that you could pass a Thread to another Thread’s constructor:
Thread t = new Thread(new MyFirstThread());

This is a bit silly, but it’s legal. In this case, you really just need a Runnnable, and creating a whole other Thread is overkill.

Besides the no-arg constructor and the constructor that takes a Runnable (the target, i.e., the instance with the job to do), there are other overloaded constructors in class Thread. The constructors we care about are
• Thread()
• Thread(Runnable target)
• Thread(Runnable target, String name)
• Thread(String name)

You need to recognize all of them for the exam! Don't worry, we’ll discuss some of the other constructors in the preceding list a little later.

So now you’ve made yourself a Thread instance, and it knows which run() method to call. But nothing is happening yet. At this point, all we’ve got is a Java object of type Thread. It is not yet a thread of execution. To get an actual thread and a new call stack—we still have to start the thread.

When a thread has been instantiated but not started (in other words, the start() method has not been invoked on the Thread instance), the thread is said to be in the new state. At this stage, the thread is not yet considered to be alive. Once the start() method is called, the thread is considered to be alive (even though the run() method may not have actually started executing yet). A thread is considered dead (no longer alive) after the run() method completes. The isAlive() method is the best way to determine if a thread has been started but has not yet completed its run() method.

Note: The getState() method is very useful for debugging, but you won’t get any questions about it in the exam.

Starting a Thread

You’ve created a Thread object and it knows its target. Now it’s time to get the whole thread thing running. It’s pretty straight forward:
t.start();

Prior to calling start() on a Thread instance, the thread (when we use lowercase t, we’re referring to the thread of execution rather than the Thread class) is said to be in the new state as we said. The new state means you have a Thread object but you don’t yet have a true thread. So what happens after you call start()?

• A new thread of execution starts (with a new call stack).
• The thread moves from the new state to the runnable state.
• When the thread gets a chance to execute, its target run() method will run.

Be sure you remember the following: You start a Thread, not a Runnable. You call start() on a Thread instance, not on a Runnable instance. The following example demonstrates what we’ve covered so far—defining, instantiating, and starting a thread:



class TestRunnable implements Runnable {

public void run() {

for(int x = 1; x < 6; x++) {
System.out.println("Runnable running");

}

}

}



public class TestMyThreads {

public static void main (String [] args) {

TestRunnable r = new TestRunnable();

Thread t = new Thread(r);

t.start();

}

}



Running the preceding code prints out exactly what you’d expect:

% java TestMyThreads

Runnable running

Runnable running

Runnable running

Runnable running

Runnable running


Exam Tip: There’s nothing special about the run() method as far as Java is concerned. Like main(), it just happens to be the name (and signature) of the method that the new thread knows to invoke. So if you see code that calls the run() method on a Runnable (or even on a Thread instance), that’s perfectly legal. But it doesn’t mean the run() method will run in a separate thread! Calling a run() method directly just means you’re invoking a method from whatever thread is currently executing, and the run() method goes onto the current call stack rather than at the beginning of a new call stack. The following code does not start a new thread of execution:
Thread t = new Thread();
t.run();
The run method will be executed but there will be no new Thread.

So what happens if we start multiple threads? We’ll run a simple example in a moment, but first we need to know how to print out which thread is executing. We can use the getName() method of class Thread, and have each Runnable print out the name of the thread executing that Runnable object’s run() method. The following example instantiates a thread and gives it a name, and then the name is printed out from the run() method:

class TestRunnableWithNames implements Runnable {
public void run() {
System.out.println("TestRunnableWithNames running");
System.out.println("Run by "
+ Thread.currentThread().getName());
}
}
public class NameThread {
public static void main (String [] args) {
TestRunnableWithNames nr = new TestRunnableWithNames();
Thread t = new Thread(nr);
t.setName("Rocky");
t.start();
}
}

Running this code produces the following, extra special, output:

% java NameThread
TestRunnableWithNames running
Run by Rocky

To get the name of a thread you call getName() on the Thread instance. But the target Runnable instance doesn’t even have a reference to the Thread instance, so we first invoked the static Thread.currentThread() method, which returns a reference to the currently executing thread, and then we invoked getName() on that returned reference.

Even if you don’t explicitly name a thread, it still has a name. Let’s look at the previous code, commenting out the statement that sets the thread’s name:

public class NameThread {
public static void main (String [] args) {
TestRunnableWithNames nr = new TestRunnableWithNames();
Thread t = new Thread(nr);
// t.setName("Rocky");
t.start();
}
}
Running the preceding code now gives us
% java NameThread
TestRunnableWithNames running
Run by Thread-0

And since we’re getting the name of the current thread by using the static Thread.currentThread() method, we can even get the name of the thread running our main code,
public class NameThreadTwo {
public static void main (String [] args) {
System.out.println("thread is "
+ Thread.currentThread().getName());
}
}

which prints out
% java NameThreadTwo
thread is main

That’s right, the main thread already has a name—main.


Starting and Running Multiple Threads


Let’s actually get down to business and get multiple threads going (more than two, that is). We already had two threads, because the main() method starts in a thread of its own, and then t.start() started a second thread. Now we’ll do more. The following code creates a single Runnable instance and three Thread instances. All three Thread instances get the same Runnable instance, and each thread is given a unique name. Finally, all three threads are started by invoking start() on the Thread instances.



class TestRunnableWithNames implements Runnable {

public void run() {

for (int x = 1; x <= 3; x++) {
System.out.println("Run by "

+ Thread.currentThread().getName()

+ ", x is " + x);

}

}

}

public class ManyNames {

public static void main(String [] args) {

// Make one Runnable

TestRunnableWithNames nr = new TestRunnableWithNames();

Thread one = new Thread(nr);

Thread two = new Thread(nr);

Thread three = new Thread(nr);



one.setName("Rocky");

two.setName("Cena");

three.setName("Triple H");

one.start();

two.start();

three.start();

}

}

Running this code might produce the following:



% java ManyNames

Run by Rocky, x is 1

Run by Triple H, x is 1

Run by Rocky, x is 2

Run by Cena, x is 1

Run by Rocky, x is 3

Run by Cena, x is 2

Run by Cena, x is 3

Run by Triple H, x is 2

Run by Triple H, x is 3



Well, at least that’s what it printed when we ran it—this time, on our machine. But the behavior you see above is not guaranteed. I repeat, “THE BEHAVIOR IS NOT GUARANTEED.” You need to know, for your future as a Java programmer as well as for the exam, that there is nothing in the Java specification that says threads will start running in the order in which they were started (in other words, the order in which start() was invoked on each thread). And there is no guarantee that once a thread starts executing, it will keep executing until it’s done. Or that a loop will complete before another thread begins. Nothing is guaranteed in the preceding code except this:


Each thread will start, and each thread will run to completion.


Within each thread, things will happen in a predictable order. But the actions of different threads can mix together in unpredictable ways. If you run the program multiple times, or on multiple machines, you may see different output. Even if you don’t see different output, you need to realize that the behavior you see is not guaranteed. Sometimes a little change in the way the program is run will cause a difference to emerge. Just for fun we bumped up the loop code so that each run() method ran the for loop 400 times rather than 3, and eventually we did start to see some wobbling:




public void run() {

for (int x = 1; x <= 400; x++) {
System.out.println("Run by "

+ Thread.currentThread().getName()

+ ", x is " + x);

}

}



Running the preceding code, with each thread executing its run loop 400 times, started out fine but then became nonlinear. Here’s just a snip from the command-line output of running that code:

Run by Rocky, x is 345

Run by Triple H, x is 313

Run by Cena, x is 341

Run by Triple H, x is 314

Run by Cena, x is 342

Run by Triple H, x is 315

Run by Rocky, x is 346

Run by Cena, x is 343

Run by Rocky, x is 347

Run by Cena, x is 344

And so on…


Notice that there’s not really any clear pattern here. If we look at only the output from Rocky, we see the numbers increasing one at a time, as expected:

Run by Rocky, x is 345

Run by Rocky, x is 346

Run by Rocky, x is 347



And similarly if we look only at the output from Cena, or Triple H. Each one individually is behaving in a nice orderly manner. But together, it is utter chaos! In the fragment above we see Rocky, then Cena, then Triple H (in the same order we originally started the threads), but then Cena moves in when it was Rocky’s turn. And then Triple H and Cena trade back and forth for a while until finally Rocky gets another chance. They jump around like this for a while after this. Eventually (after the part shown above) Rocky finishes, then Triple H, and finally Cena finishes with a long sequence of output. So even though Triple H was started third, he actually completed second. And if we run it again, we’ll get a different result.


Why?


Because it’s up to the scheduler, and we don’t control the scheduler! Which brings up another key point to remember: Just because a series of threads are started in a particular order doesn’t mean they’ll run in that order. For any group of started threads, order is not guaranteed by the scheduler. And duration is not guaranteed. You don’t know, for example, if one thread will run to completion before the others have a chance to get in or whether they’ll all take turns nicely, or whether they’ll do a combination of both.


A thread is no longer a thread when its run() method completes execution.


When a thread completes its run() method, the thread ceases to be a thread of execution. The stack for that thread dissolves, and the thread is considered dead. Not dead and gone, but, just dead. It’s still a Thread object, just not a thread of execution. So if you’ve got a reference to a Thread instance, then even when that Thread instance is no longer a thread of execution, you can still call methods on the Thread instance, just like any other Java object. What you can’t do, though, is call start() again.


Once a thread has been started, it can never be started again.


If you have a reference to a Thread, and you call start(), it’s started. If you call start() a second time, it will cause an exception (an IllegalThreadStateException, which is a kind of RuntimeException, but you don’t need to worry about catching it). This happens whether or not the run() method has completed from the first start() call. Only a new thread can be started, and then only once. A runnable thread or a dead thread cannot be restarted.



Exam Tip: In addition to using setName() and getName to identify threads, you might see getld(). The getld() method returns a positive, unique, long number, and that number will be that thread’s only ID number for the thread’s entire life

The Thread Scheduler

The thread scheduler is the part of the JVM that decides which thread should run at any given moment, and also takes threads out of the run state. Assuming a single processor machine, only one thread can actually run at a time. Only one stack can ever be executing at one time. And it’s the thread scheduler that decides which thread among all other eligible threads will actually run. When I say eligible, it means threads that are in the runnable state.

Any thread in the runnable state can be chosen by the scheduler to be the one and only running thread. If a thread is not in a runnable state, then it cannot be chosen to be the currently running thread. And, to repeat something I have already said:

THE ORDER IN WHICH RUNNABLE THREADS ARE CHOSEN TO RUN IS NOT AND I MEAN IS NOT GUARANTEED!


Although we don’t control the thread scheduler, we can sometimes influence it. The following methods give us some tools for influencing the scheduler. Just don’t ever mistake influence for control.

Exam Tip: Expect to see exam questions that look for your understanding of what is and is not guaranteed! You must be able to look at thread code and determine whether the output is guaranteed to run in a particular way or is indeterminate

Methods from the java.lang.thread Class

Some of the methods that can help us influence thread scheduling are as follows:
public static void sleep(long millis) throws InterruptedException
public static void yield()
public final void join() throws InterruptedException
public final void setPriority(int newPriority)

Note that both sleep() and join() have overloaded versions which not shown here.

Methods from the java.lang.object Class

Every class in Java inherits the following three thread-related methods:
public final void wait() throws InterruptedException
public final void notify()
public final void notifyAll()

The wait() method has three overloaded versions (including the one listed here).

Don't worry too much about the functionalities of these methods because we are going to have a full chapter dedicated to these babies.

For now, the chapter is over. You can give a sigh of relief which is exactly what I am doing now.. Huh!!!

Previous Chapter: Self Test - Chapters 52 to 55

Next Chapter: Thread States & Transition

No comments:

Post a Comment

© 2013 by www.inheritingjava.blogspot.com. All rights reserved. No part of this blog or its contents may be reproduced or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without prior written permission of the Author.

ShareThis

Google+ Followers

Followers