Section 14.2. What Is a Thread?


[Page 658 (continued)]

14.2. What Is a Thread?

A thread (or thread of execution or thread of control) is a single sequence of executable statements within a program. For Java applications, the flow of control begins at the first statement in main() and continues sequentially through the program statements. For Java applets, the flow of control begins with the first statement in init(). Loops within a program cause a certain block of statements to be repeated. If-else structures cause certain statements to be selected and others to be skipped. Method calls cause the flow of execution to jump to another part of the program, from which it returns after the method's statements are executed. Thus, within a single thread, you can trace the sequential flow of execution from one statement to the next.

One way to visualize a thread is to imagine that you could make a list of the program's statements as they are executed by the computer's central processing unit (CPU). Thus, for a particular execution of a program with loops, method calls, and selection statements, you could list each instruction that was executed, beginning at the first, and continuing until the program stopped, as a single sequence of executed statements. That is a thread!

Visualizing a thread


Now imagine that we break a program up into two or more independent threads. Each thread will have its own sequence of instructions. Within a single thread, the statements are executed one after the other, as usual. However, by alternately executing the statements from one thread and another, the computer can run several threads concurrently. Even though the CPU executes one instruction at time, it can run multiple threads concurrently by rapidly alternating among them. The main advantage of concurrency is that it allows the computer to do more than one task at a time. For example, the CPU could alternate between downloading an image from the Internet and running a spreadsheet calculation. This is the same way you ate toast and cereal and drank coffee in our earlier breakfast example. From our perspective, it might look as if the computer had several CPUs working in parallel, but that's just the illusion created by effectively scheduling threads.


[Page 659]

Java Language Rule: JVM Threads

The Java Virtual Machine (JVM) is itself an example of a multithreaded program. JVM threads perform tasks that are essential to the successful execution of Java programs.


Java Language Rule: Garbage Collector Thread

One of the JVM threads, the garbage collector thread, automatically reclaims memory taken up by objects that are not used in your programs. This happens at the same time that the JVM is interpreting your program.


14.2.1. Concurrent Execution of Threads

The technique of concurrently executing several tasks within a program is known as multitasking. A task in this sense is a computer operation of some sort, such as reading or saving a file, compiling a program, or displaying an image on the screen. Multitasking requires the use of a separate thread for each of the tasks. The methods available in the Java Thread class make it possible (and quite simple) to implement multithreaded programs.

Multitasking


Most computers, including personal computers, are sequential machines that consist of a single CPU capable of executing one machine instruction at a time. In contrast, parallel computers, used primarily for large-scale scientific and engineering applications, are made up of multiple CPUs working in tandem.

Today's personal computers, running at clock speeds over 1 gigahertz (i.e., more than 1 billion cycles per second) are capable of executing millions of machine instructions per second. Despite its great speed, however, a single CPU can process only one instruction at a time.

A CPU uses a fetch-execute cycle to retrieve the next instruction from memory and execute it. Since CPUs can execute only one instruction at a time, multithreaded programs are made possible by dividing the CPU's time and sharing it among the threads. The CPU's schedule is managed by a scheduling algorithm, which is an algorithm that schedules threads for execution on the CPU. The choice of a scheduling algorithm depends on the platform on which the program is running. Thus, thread scheduling is handled differently on Unix, Windows, and Macintosh systems.

One common scheduling technique is known as time slicing, in which each thread alternately gets a slice of the CPU's time. For example, suppose we have a program that consists of two threads. Using this technique, the system would give each thread a small quantum of CPU timesay, one thousandth of a second (1 millisecond)to execute its instructions. When its quantum expires, the thread would be preempted and the other thread would be given a chance to run. The algorithm would then alternate in this round-robin fashion between one thread and the other (Fig. 14.1). During each millisecond on a 300-megahertz CPU, a thread can execute 300,000 machine instructions. One megahertz equals 1 million cycles per second. Thus, within each second of real time, each thread will receive 500 time slices and will be able to execute something like 150 million machine instructions.

Figure 14.1. Each thread gets a slice of the CPU's time.
(This item is displayed on page 660 in the print version)


CPUs are sequential


Time slicing


Under priority scheduling, threads of higher priority are allowed to run to completion before lower-priority threads are given a chance. An example of a high-priority thread would be one that is processing keyboard input or any other interactive input from the user. If such tasks were given low priority, users would experience noticeable delays in their interaction, which would be quite unacceptable.


[Page 660]

Priority scheduling


The only way a high-priority thread can be preempted is if a thread of still higher priority becomes available to run. In many cases, higher-priority threads can complete their task within a few milliseconds, so they can be allowed to run to completion without starving the lower-priority threads. An example would be processing a user's keystroke, a task that can begin as soon as the key is struck and can be completed very quickly. Starvation occurs when one thread is repeatedly preempted by other threads.

Java Language Rule: Thread Support

Depending on the hardware platform, Java threads can be supported by assigning different threads to different processors, by time slicing a single processor, or by time slicing many hardware processors.


14.2.2. Multithreaded Numbers

Let's consider a simple example of a threaded program. Suppose we give every individual thread a unique ID number, and each time it runs, it prints its ID 10 times. For example, when the thread with ID 1 runs, the output produced would just be a sequence of 10 1's: 1111111111.

As shown in Figure 14.2, the NumberThread class is defined as a subclass of Thread and overrides the run() method. To set the thread's ID number, the constructor takes a single parameter that is used to set the thread's ID number. In the run() method, the thread simply executes a loop that prints its own number 10 times:


[Page 661]

public class NumberThread extends Thread {     int num;     public NumberThread(int n) {         num = n;     }     public void run() {         for (int k=0; k < 10; k++) {             System.out.print(num);         } // for     } // run() } // NumberThread class 


Figure 14.2. The NumberThread class overrides the inherited run() method.
(This item is displayed on page 660 in the print version)


Now let's define another class whose task will be to create many NumberThreads and get them all running at the same time (Fig. 14.3). For each NumberThread, we want to call its constructor and then start() it:

public class Numbers {   public static void main(String args[]) {                                                  // 5 threads     NumberThread number1, number2, number3, number4, number5;                                                  // Create and start each thread     number1 = new NumberThread(1); number1.start();     number2 = new NumberThread(2); number2.start();     number3 = new NumberThread(3); number3.start();     number4 = new NumberThread(4); number4.start();     number5 = new NumberThread(5); number5.start();   } // main() } // Numbers class 


Figure 14.3. The Numbers object creates several instances of NumberThread and tells each one to start().


Thread subclass


When a thread is started by calling its start() method, it automatically calls its run() method. The output generated by this version of the Numbers application is as follows:

11111111112222222222333333333344444444445555555555


Starting a thread


From this output, it appears that the individual threads were run in the order in which they were created. In this case, each thread was able to run to completion before the next thread started running.


[Page 662]

What if we increase the number of iterations that each thread performs? Will each thread still run to completion? The following output was generated for 200 iterations per thread:

111111111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111111111111111111111111111111111112222222 222222222222222222222222222222222222222222222222222222222222222222222 222222222222222222222222222222222222222222222222222222222222222222222 222222222222222222222222222222222222222222223333333333333333333333333 333333333333333333333333333333333333333333333333333333333333333333333 333333333333333333333333333444444444444444444444444444444444444444444 444444444444444444444444444444444444444444444444444444444444444444444 444444444455555555555555555555555555555555555555555555555555555555555 555555555555555555555555555555555555555555555555555555555555552222222 222233333333333333333333333333333333333333333333333333333333333333333 333333333333334444444444444444444444444444445555555555555555555555555 555555555555555555555555555555555555555555555555555555444444444444444 4444444444444444444444444444444444 


In this case, only thread 1 managed to run to completion. Threads 2, 3, 4, and 5 did not. As this example illustrates, the order and timing of a thread's execution are highly unpredictable. The example also serves to illustrate one way of creating a multithreaded program:

  • Create a subclass of the Thread class.

  • Within the subclass, implement a method with the signature void run() that contains the statements to be executed by the thread.

  • Create several instances of the subclass and start each thread by invoking the start() method on each instance.

Java Language Rule: Thread Creation

One way to create a thread in Java is to define a subclass of Thread and override the default run() method.





Java, Java, Java(c) Object-Orienting Problem Solving
Java, Java, Java, Object-Oriented Problem Solving (3rd Edition)
ISBN: 0131474340
EAN: 2147483647
Year: 2005
Pages: 275

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