Showing posts with label Studies. Show all posts
Showing posts with label Studies. Show all posts

Friday, March 23, 2012

Chapter 4: The Lifecycle of a Thread


Now that we know how to create a thread, the next step in the learning process it to understand the lifecycle of a thread. 

Before we get started: 

The class Thread contains many methods that might affect the life of a thread. They are: 
public void start( );
public void run( );
public void stop( ); // Deprecated, do not use
public void resume( ); // Deprecated, do not use
public void suspend( ); // Deprecated, do not use
public static void sleep(long millis);
public static void sleep(long millis, int nanos);
public boolean isAlive( );
public void interrupt( );
public boolean isInterrupted( );
public static boolean interrupted( );
public void join( ) throws InterruptedException;

Each of these methods has a purpose and an effect on the thread when used. 

Creating a Thread

The first phase in this lifecycle is thread creation. Threads are represented by instances of the Thread class, so creating a thread is done by calling a constructor of that class. In our example, we use the simplest constructor available to us. Additional constructors of the Thread class allow you to specify the thread's name or a Runnable object to serve as the thread's target.

All threads have names that serve to identify them in the virtual machine. By default, that name consists of information about the thread: its priority, its thread group, and other thread information. If you like, you can give a thread a different name, perhaps one that will have meaning to you if you print it out. 

Starting a Thread

A thread exists once it has been constructed, but at that point it is not executing any code. The thread is in a waiting state.

In this waiting state, other threads can interact with the existing thread object. Various attributes of the waiting thread can be set: its priority, its name, its daemon status, and so on. Therefore, even though the thread is waiting, its state may be changed by other threads.
When you're ready for the thread to begin executing code, you call its start() method. This method performs some internal housekeeping and calls the thread's run() method. When the start() method returns, two threads are now executing in parallel: the original thread (which has returned from calling the start() method) and the newly started thread (which is now executing its run() method).

After its start() method has been called, the new thread is said to be alive. In fact, the Thread class has an isAlive() method that tells you the state of the thread: if the isAlive() method returns true, the thread has been started and is executing its run() method. If the isAlive( ) method returns false, however, the thread may not be started yet or may be terminated.

Terminating a Thread

Once started, a thread executes only one method: the run() method. The run() method may be very complicated, it may execute forever, and it may call millions of other methods. Regardless of all this, once the run() method finishes executing, the thread has completed its execution. Like all Java methods, the run() method finishes when it executes a return statement, when it executes the last statement in its method body, or when it throws an exception or fails to catch an exception thrown to it.

As a result, the only way to terminate a thread is to arrange for its run() method to complete. If you look at the documentation of the Thread class, you notice that the class contains a stop() method which seems like it might be used to terminate a thread. It turns out that the stop() method has an inherent problem (an internal race condition, we will see what a race condition is very soon). As a result, the stop() method is deprecated and should not be used. Some Java implementations prohibit its use directly, and the security manager can also be used to prohibit programs from calling it.

There are many threads that you don't need to stop. Often, threads are performing a fixed task, and you always want the task to run to completion. In other cases, the thread can run until the application exits (e.g., when we call the System.exit() method in response to the user pressing the Quit button).

Often, however, you want a thread to continue to execute until some other condition is met. 
The run() method cannot throw a checked exception, but like all Java methods, it can throw an unchecked exception. Throwing an unchecked exception (an exception that extends the RuntimeException class)—or failing to catch a runtime exception thrown by something the run( ) method has called—also causes a thread to stop. 

Pausing, Suspending, and Resuming Threads

Once a thread begins executing its run() method, it continues execution until the run() method completes. If you're familiar with other thread models, you may know of a concept called thread suspension, where a thread is told to pause its execution. Later, the thread is resumed, which is to say that it is told to continue its execution. The Thread class contains suspend() and resume() methods, but they suffer from the same race condition problem as the stop() method, and they, too, are deprecated.

It is possible for a thread to suspend its own execution for a specific period of time by calling the sleep() method. When a thread executes the sleep() method, it pauses for a given number of milliseconds, during which it is said to be asleep. When the pause time has elapsed, the thread wakes up and continues execution with the statements immediately following the sleep() method.

Trivia:
The Thread class provides a version of the sleep() method that allows the developer to specify the time in nanoseconds. Most Java virtual machines do not support this sort of precise timing. When the sleep() method executes, it rounds the nanosecond argument to the nearest millisecond. In fact, most operating systems then further adjust the millisecond argument so that it is a multiple of some number: e.g., 20 or 50 milliseconds. Consequently, the least amount of time that you can sleep on most Java implementations is 20 or 50 milliseconds.
Note that this is true even in J2SE 5.0, which introduces other nanosecond time functionality (e.g., the System.nanoTime() method). The resolution of the sleep( ) method is still only good to a few milliseconds.

Strictly speaking, sleeping is not the same thing as thread suspension. One important difference is that with true thread suspension, one thread would suspend (and later resume) another thread. Conversely, the sleep() method affects only the thread that executes it; it's not possible to tell another thread to go to sleep.

Threads can use the wait and notify mechanism to achieve the functionality of thread suspension and resumption. The difference is that the threads must be coded to use that technique (rather than a generic suspend/resume mechanism that could be imposed from other threads). Don't worry much about the wait and notify just yet. We will cover it in detail very soon. 

Thread Cleanup

A thread that has completed its run() method has terminated. It is no longer active (the isAlive() method returns false). However, the thread object itself may be holding interesting information. As long as some other active object holds a reference to the terminated thread object, other threads can execute methods on the terminated thread and retrieve that information. If the thread object representing the terminated thread goes out of scope, the thread object is garbage collected. On some platforms, this also has the effect of cleaning up system resources associated with the thread.

In general, then, you should not hold onto thread references so that they may be collected when the thread terminates.

One reason to hold onto a thread reference is to determine when it has completed its work. That can be accomplished with the join() method. The join() method is often used when you have started threads to perform discrete tasks and want to know when the tasks have completed. 
The join() method blocks until the thread has completed its run() method. If the thread has already completed its run() method, the join() method returns immediately. This means that you may call the join() method any number of times to see whether a thread has terminated. Be aware, though, that the first time you call the join() method, it blocks until the thread has actually completed. You cannot use the join() method to poll a thread to see if it's running (instead, use the isAlive() method just discussed). 

Monday, March 12, 2012

Chapter 3: Creating a Thread


Creating a Thread is fairly simple. The complexities get introduced once you have your threads running and then you see that the output isn’t what you exactly expected. Well, I don’t want to jump ahead too much. We will learn all those complexities one by one. For now lets keep our focus on how to create threads. 

You can create threads in two ways: 

1. By Extending the Thread Class 
2. By Implementing the Runnable Interface 

Common sense related to Java Programming tells us that using the interface may be a better option because, your class may need to inherit features from its parent class or such a feature may be required in future. Either ways, it would be a better idea to implement the Runnable Interface so that our classes can do, what they were designed/created to do. 

Am gonna keep this chapter short and sweet because, I have already explained about how to Create a Thread extensively during the chapters on the SCJP Certification series



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);
}
}

NoteThe 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. 

NoteThe 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!!! 

ShareThis

 
View My Stats