Lecture 13

Threads

Funny thing about life – we all appear to run in a separate thread. Yes, we all appear to run in a separate thread. People perform different actions simultaneously or in parallel.

Initially programs that people wrote where single threaded, they had a definitive control flow, which was judiciously followed. As our computer science knowledge advanced we created software and hardware parallelism. One of the basic examples of multitasking program are operating systems. On Unix for example different users can run several programs at once. The slight difference between Unix and real life is that Unix scheduler interrupts processes and resumes them very often, so that they appear to be running all at once. In real life, at least as far as we perceive, we are all running all the time (but who knows ?). The idea of threads is crucial when it comes to large contemporary systems. It is often necessary for such system to perform several tasks simultaneously.

For example httpd daemon spawns a thread to handle next web browser client. Can you imagine what it would be like if only one user at a time could access a site? Nobody would want to browse Yahoo! So there is a main httpd process that acts as a server (it listens on a certain port, usually 80). As soon as some client connects to it, it creates a child process or spawns another thread, which handles this clients’ request separately. Many of you are familiar with fork system call in C. The idea is similar to threading but the mechanism is really different. With regular fork on Unix completely new (child) process is created, which is essentially a snapshot of parents environment at the moment. You remember that in your C code you had to encode an if parent do this if child do that, because the same executable was used for parent and child. Fork is very powerful, but expensive. Instead, newer operating systems (Solaris, Windows NT) offer threads.

A thread is really just specific execution sequence. In most implementations, every thread has its own stack, but shares heap with all other threads. Why is that? Well, think about it this way. There are four members in your project team, each is doing particular piece of code for the project. When all are done the project leader compiles all the code together and submits it. As long as you agreed on an interface before hand, you can work independently. If you have a global variable count set to 4, you can decrement when you are done. Thus project leader will know when to collect the code. Common heap provides a way for threads to communicate with each other, if necessary. One must be very careful when updating common data structures in multithreaded environment. There are several synchronization mechanisms for ensuring that shared data is accessed in the mutually exclusive way (semaphores, critical sections, monitors, etc.)

What is really nice about Java is that threads are part of the language! The reason that this was possible is that Java has its own virtual machine. Regardless of whether underlying operating system offers threads or not, Java programs are multithreaded. However, in order for threads to be really useful, we should be running on the operating system that supports threads and preferably on a machine with several CPU.

Just as you would expect, Thread in Java is a class. Actually there are two ways to do threads in Java. One way is to extends the Thread class, another is to implement Runnable interface. The reason for it is that, as you know, Java doesn’t allow multiple class inheritance. So if you have to inherit from some other class you can still run in a separate thread if you implement Runnable interface. First lets see how you may use the Thread class

Produces something like this:

What we have to do in order extend Thread is just create class which has a run method. That's it. The Thread class defines method start which we don't have to override, which will call the run method.The way Java threads are organized, run method performs all the work of a thread. When run() method completes, the thread automatically handles cleanup process & other things that it needs to do before the termination. It is possible to terminate Thread abruptly, without any cleanup by calling destroy(). This is rather unfriendly way of stopping the thread and should not be used. The alternative is to call stop(), which will perform cleanup before destroying the thread. Even stop is dangerous according to latest VM specifications. Java API states that Thread will stop correctly when the run method of its Runnable object will be over.

Before we learn more intricases of thread coding in Java, let us see how we can create threads using Runnable interface instead of extending thread class

When you run this you get the same output as if you ran the previous class. The idea is this. Notice that in the main we construct Thread with Runnable object. Thread just simply offers a constructor, which acccepts instance of Runnable. Then, thread's run method looks like this

So which one should we use? The answer in most cases would be Runnable. We are not really enchacing Thread class, so we shouldn't be inheriting from it. Also, most of the time, the classes that you want to run in a separate thread will be performing other tasks and may need to inherit from other classes. Thus, Runnable is a preferred choice.

So what are the threads used by any Java program? The System threads include the following

- Clock handler
This thread is concerned with timer events internal to virtual machine.
This thread takes care of calls like sleep and such.

- Idle thread
This thread has very low priority within virtual machine. It will only run when
all other threads are idle. The primary use of this thread is to let the garbage
collector know that it is a good time to start collecting unreferenced objects.

- Garbage collector
Garbage collector looks for objects which are no longer referenced, or for sequences
of objects with looping references and removes them from the memory. The garbage
collector sleeps for 1 second, then wakes up and checks if the idle thread has ran.
If it did then it starts scanning the memory.

- Finalizer thread
The finalizer thread simply calls finalize on the objects that garbage collector
has freed (Think about why aren't these two united into one thread?)

Finally, you have

- Main thread
Default thread of the application, which executes starting at the main()

Here is how you can get all active Threads

Let us now dive in into more intricate details about threads. Suppouse that you need one thread in your application for wait for completion of another thread. You can acomplish this using join method.

Results in the following

Is it possible to restart a thread, i.e. can we call start, stop and then start again? The answer is no. Actually the answer is almost always no. It actually takes time for thread to stop, so if the start called during that time there is a slight chance that it would happen. However, it is a bad idea. Once the thread is stoppped, there is no way to restart it.

Thread synchronization

Of course we would like threads to cooperate. We want to accomplish things more elegantly and efficiently, by have several threads in our applications, whenever appropriate. However, we must be aware of certain consequeces that may arise if we are not really careful. Threads are really powerful but must be used with care. Java offers very nice and simple synchronization constructs. You can declare a method of a class to be synchronized,which simply means that only one thread can be running this method at a time. You can also declare a synchronized block, which means that you are locking an object for the duration of this block, and only one thread can be executing it. By declaring class synchronized, we are making all of its methods synchronized.

Sometimes, when you are not too careful, your program may enter the state of deadlock - when to threads are waiting for resource consumed by the other

This results in

So the deadlock occurs in the following setup: Thread one called synchronized method on object X which called synchronized method on object Y, while thread two called synchronized method on object Y first, which in turn calls the syncronized method method on object X.

Instead of calling sleep above we could have used another Thread routine - yield. this simply surrenders the execution of the current thread and runs another thread.

Finally, lets look at the mechanisms of cooperation between threads. Suppose that we would want to have another kind of the ObjectPool. When user calls acquire and the pool is empty we want to wait until pool has something.

Producer/Consumer example with Threads can be found off the main page in the code section.

Please read

Chapter on Threads from Arnold & Gosling's book