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
- Idle thread
- Garbage collector
- Finalizer thread
Finally, you have - Main thread
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 |