18.1 CREATING AND EXECUTING SIMPLE THREADS IN JAVA


18.1 CREATING AND EXECUTING SIMPLE THREADS IN JAVA

Before showing how one can write a multithreadable program in Java, we wish to point out that even the simplest Java programs execute in a multithreaded mode without any conscious effort by the programmer. That's because the garbage collection for reclaiming the dynamically allocated memory occupied by unreferenced objects takes place in a separate low-priority thread running in the background.[3] To see all of the threads running as you execute any Java class, you could embed the following two statements in the main of your program:

      ThreadGroup  topGroup =           Thread.currentThread().getThreadGroup().getParent();      topGroup.list(); 

where, as the method names imply, we first get the thread group of which the thread executing the main is a member, then we get its parent thread group, and, finally, in the second statement we ask the system to display the thread group tree. The reason for why we need to get hold of the ThreadGroup object is that the threads are organized in the form of a tree of thread groups. (Of course, it is possible for a thread group to consist of a single thread.) To actually see an example of this thread-group tree, you can run the following program which does nothing but display the currently running threads in the Java Virtual Machine:

 class DisplayThreads {      public static void main( String[] args )      {           ThreadGroup topGroup =                Thread.currentThread().getThreadGroup().getParent();           topGroup.list();      } } 

Compiling and running this program on a Linux machine yields the following output:

 java.lang.ThreadGroup [name=system,maxpri=10]     Thread [Reference Handler,10,system]     Thread [Finalizer,8,system]     Thread [Signal Dispatcher,10,system]     Thread [CompileThread0,10,system]     java.lang.ThreadGroup [name=main,maxpri=10]         Thread [main,5,main] 

As shown by the names of the threads inside the square brackets, at the top we have a system thread group. This thread group consists of a "Reference Handler" thread for keeping track of object reference counts, a "Finalizer" thread for automatic garbage collection, a "Signal Dispatcher" thread in which the Event Dispatch Thread (see Section 18.11) is run, a "CompileThreadO" for JIT compilation we talked about inChapter 2, and, finally, another thread group called"main." The thread group "main" has a single thread running in it, also called "main."

That brings us to the main goal of this section, which is to show how to create and execute a simple multithreadable program in Java. To be multithreadable, a class must extend the class Thread of the java.lang package and override its run method. The code that needs to be run in the form of separate threads is placed in the run method. This code is invoked for execution by calling the start method.

To illustrate Java multithreading in its most rudimentary form, the following example shows a multithreadable class HelloThread.[4] All that the run method of this class does is to print out a message that is supplied as an argument to the class constructor. In the main of this class, we create three threads, and start the execution of each thread by invoking start on each. The overall goal of the program is to print out the following string on the terminal:

                 Good morning to you! 

The first three words of this string will be printed out by the three child threads created in main and the last, you! by the parent thread (or the process) in which main is being executed.

 
//ThreadsBasic.java class HelloThread extends Thread { String message; HelloThread( String message ) { this.message = message; } public void run() { //int sleeptime = (int) ( Math.random () * 3000 ); //(A) //try { //(B) // sleep( sleeptime ); //(C) //} catch( InterruptedException e ){} //(D) System.out.print( message ); } public static void main( String[] args ) { HelloThread ht1 = new HelloThread( "Good" ); HelloThread ht2 = new HelloThread( "morning" ); HelloThread ht3 = new HelloThread( "to" ); ht1.start(); ht2.start(); ht3.start(); try { //(E) sleep( 1000 ); //(F) } catch( InterruptedException e ){} //(G) System.out.println( " you!" ); } }

If you compile and run this program, it will in most cases print out the Good morning to you! string. But, at least theoretically, there would be no guarantee that the words would be printed out in the correct order. Since the three threads and the process in which they reside will be executed concurrently, the words could be printed out in a jumbled order. To underscore this point, suppose we comment out the try-catch block in lines (E) through (G), in most cases the word you! would be printed out before one or more of the three other words. Also, if you uncomment the commented out block in lines (A) through (D) to introduce random sleep times in each of the threads, the words would definitely be printed out in a jumbled order. The sleep times introduced in each thread could correspond to some background activity that a thread might engage in before executing output producing code.

As soon as a thread is created by invoking the constructor of the HelloThread class, the thread is considered to be in the born state. And then when the method start is invoked on the thread, it is in the runnable state and waiting for the scheduler to assign it a processor. Taking thread priorities into account, which can be set by invoking setPriority on a thread, the scheduler assigns the processor to each thread for a fixed interval of time, called a quantum. Assigning quanta of time to the different threads is referred to as timeslicing.

As mentioned previously, a serious problem with the Hello Thread class is that the three threads and the parent thread are all competing with one-another for time on the processor and there is no guarantee which will finish first. One simple way to regulate this race condition is to invoke join on another thread, as we do in lines (H) through (J) in the following version of the program. The thread that invokes join on another thread has its own execution halted until the thread on which join was invoked has finished its execution. In the code presented below, main is invoking join on the threads ht1, ht2, and ht3. This implies that the execution of main will come to a halt until the threads ht1, ht2, and ht3 are done.

Using join in this manner will at least guarantee that the word you! will be printed out after the other three words of the message. But we still have no guarantee that the first three words will be printed out in the correct order. But, from the standpoint of general multithreaded programming, we would not want that sort of a guarantee anyway because concurrency through multithreading is not intended for the sort of serial control you need to get the words to print out in the correct order. It is for this reason we said at the very beginning of this section that HelloThread was an inappropriate example for multithreading even though it gave us a compact example for illustrating the more basic notions of multithreading in Java.

 
//ThreadsBasicWithJoin.java class HelloThread extends Thread { String message; HelloThread( String message ) { this.message = message; } public void run() { //int sleeptime = (int) ( Math.random() * 3000 ); //try { // sleep( sleeptime ); // } catch( InterruptedException e ){} System.out.print( message ); } public static void main(String[] args) throws InterruptedException { HelloThread ht1 = new HelloThread( "Good" ); HelloThread ht2 = new HelloThread( " morning" ); HelloThread ht3 = new HelloThread( " to" ); ht1.start(); ht2.start(); ht3.start(); ht1.join(); //(H) ht2.join(); //(I) ht3.join(); //(J) System.out.println( " you!" ); } }

[3]Compare this with C++, where a programmer must explicitly invoke the destructor of an object to free up the dynamically allocated memory. Should a programmer forget to do so, a program could potentially suffer from memory leaks. Java's automatic garbage collection mechanism running in a separate thread, although not as efficient as the deliberate and immediate freeing up of memory in C++, results nonetheless in simplification of the programming effort.

[4]Strictly speaking, this is an inappropriate example for multithreading since the words of the message must be printed out in a specific order. Nonetheless, this class will help us convey in a compact fashion some of the more basic aspects of multithreading.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net