Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Java How to Program, Fourth Edition - Deitel H., Deitel P.pdf
Скачиваний:
63
Добавлен:
24.05.2014
Размер:
14.17 Mб
Скачать

Chapter 15

Multithreading

841

caller immediately. The caller then executes concurrently with the launched thread. The start method throws an IllegalThreadStateException if the thread it is trying to start has already been started.

The static method sleep is called with an argument specifying how long the currently executing thread should sleep (in milliseconds); while a thread sleeps, it does not contend for the processor, so other threads can execute. This can give lower priority threads a chance to run.

The interrupt method is called to interrupt a thread. The static method interrupted returns true if the current thread has been interrupted and false otherwise. A program can invoke a specific thread’s isInterrupted method to determine whether that thread has been interrupted.

Method isAlive returns true if start has been called for a given thread and the thread is not dead (i.e., its controlling run method has not completed execution).

Method setName sets a Thread’s name. Method getName returns the name of the Thread. Method toString returns a String consisting of the name of the thread, the priority of the thread and the thread’s ThreadGroup (discussed in Section 15.11).

The static method currentThread returns a reference to the currently executing Thread.

Method join waits for the Thread to which the message is sent to die before the calling Thread can proceed; no argument or an argument of 0 milliseconds to method join indicates that the current Thread will wait forever for the target Thread to die before the calling Thread proceeds. Such waiting can be dangerous; it can lead to two particularly serious problems called deadlock and indefinite postponement. We will discuss these momentarily.

Testing and Debugging Tip 15.2

Method dumpStack is useful for debugging multithreaded applications. A program calls static method dumpStack to print a method-call stack trace for the current Thread.

15.3 Thread States: Life Cycle of a Thread

At any time, a thread is said to be in one of several thread states (illustrated in Fig. 15.1). Let us say that a thread that was just created is in the born state. The thread remains in this state until the program calls the thread’s start method, which causes the thread to enter the ready state (also known as the runnable state). The highest priority ready thread enters the running state (i.e., the thread begins executing), when the system assigns a processor to the thread. A thread enters the dead state when its run method completes or terminates for any reason—a dead thread eventually will be disposed of by the system.

One common way for a running thread to enter the blocked state is when the thread issues an input/output request. In this case, a blocked thread becomes ready when the I/O for which it is waiting completes. A blocked thread cannot use a processor even if one is available.

When the program calls method sleep in a running thread, that thread enters the sleeping state. A sleeping thread becomes ready after the designated sleep time expires. A sleeping thread cannot use a processor even if one is available. If the program calls method interrupt on a sleeping thread, that thread exits the sleeping state and becomes ready to execute.

842

Multithreading

Chapter 15

born

start

 

 

 

 

 

y

 

 

 

 

 

A

l

l

 

 

 

 

f

 

 

 

 

y

 

 

 

 

i

 

 

 

f

 

 

 

 

t

 

 

 

i

 

 

 

 

o

 

 

 

t

 

 

 

 

n

 

 

 

o

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

o

 

 

 

 

 

 

 

 

 

quantum expiration

yield interrupt

 

 

t

 

 

 

 

 

 

i

 

 

 

 

p

a

 

 

 

 

 

w

 

 

 

 

e

e

 

 

 

 

 

l

 

 

 

 

s

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ready

dispatch (assign a processor)

running

 

i

 

 

 

 

s

 

 

 

 

s

 

c

 

 

u

e

 

 

 

 

I

 

 

 

 

/

o

 

 

 

O

m

 

 

 

 

p

 

 

 

 

l

 

 

 

 

e

 

 

 

 

t

 

 

 

 

e

 

 

I /

O

c

o

m

p l e

t i

o

n

request

waiting

sleeping

dead

blocked

sleep interval expires

Fig. 15.1 State diagram showing the Life cycle of a thread.

When a running thread calls wait, the thread enters a waiting state for the particular object on which wait was called. One thread in the waiting state for a particular object becomes ready on a call to notify issued by another thread associated with that object. Every thread in the waiting state for a given object becomes ready on a call to notifyAll by another thread associated with that object. The wait, notify and notifyAll methods will be discussed in more depth shortly, when we consider monitors.

A thread enters the dead state when its run method either completes or throws an uncaught exception.

15.4 Thread Priorities and Thread Scheduling

Every Java applet or application is multithreaded. Every Java thread has a priority in the range Thread.MIN_PRIORITY (a constant of 1) and Thread.MAX_PRIORITY (a constant of 10). By default, each thread is given priority Thread.NORM_PRIORITY (a constant of 5). Each new thread inherits the priority of the thread that creates it.

Some Java platforms support a concept called timeslicing and some do not. Without timeslicing, each thread in a set of equal-priority threads runs to completion (unless the thread leaves the running state and enters the waiting, sleeping or blocked state, or the

Chapter 15

Multithreading

843

thread gets interrupted by a higher priority thread) before that thread’s peers get a chance to execute. With timeslicing, each thread receives a brief burst of processor time called a quantum during which that thread can execute. At the completion of the quantum, even if that thread has not finished executing, the operating system takes the processor away from that thread and gives it to the next thread of equal priority (if one is available).

The job of the Java scheduler is to keep the highest priority thread running at all times, and if timeslicing is available, to ensure that several equally high-priority threads each execute for a quantum in round-robin fashion (i.e., these threads can be timesliced). Figure 15.2 illustrates Java’s multilevel priority queue for threads. In the figure, threads A and B each execute for a quantum in round-robin fashion until both threads complete execution. Next, thread C runs to completion. Then, threads D, E and F each execute for a quantum in round-robin fashion until they all complete execution. This process continues until all threads run to completion. Note that new higher-priority threads could postpone— possibly indefinitely—the execution of lower priority threads. Such indefinite postponement often is referred to more colorfully as starvation.

A thread’s priority can be adjusted with method setPriority, which takes an int argument. If the argument is not in the range 1 through 10, setPriority throws an IllegalArgumentException. Method getPriority returns the thread’s priority.

A thread can call the yield method to give other threads a chance to execute. Actually, whenever a higher priority thread becomes ready, the operating system preempts the current thread. So, a thread cannot yield to a higher priority thread, because the first thread would have been preempted when the higher priority thread became ready. Similarly, yield always allows the highest priority-ready thread to run, so if only lower priority threads are ready at the time of a yield call, the current thread will be the highest priority thread and will continue executing. Therefore, a thread yields to give threads of an equal priority a chance to run. On a timesliced system this is unnecessary, because threads of equal priority will each execute for their quantum (or until they lose the processor for some other reason), and other threads of equal priority will execute in roundrobin fashion. Thus yield is appropriate for nontimesliced systems in which a thread would ordinarily run to completion before another thread of equal priority would have an opportunity to run.

Performance Tip 15.3

On nontimesliced systems, cooperating threads of equal priority should periodically call yield to enable their peers to proceed smoothly.

Portability Tip 15.2

Java applets and applications should be programmed to work on all Java platforms to realize Java’s goal of true portability. When designing applets and applications that use threads, you must consider the threading capabilities of all the platforms on which the applets and applications will execute.

A thread executes unless it dies, it becomes blocked by the operating system for input/ output (or some other reason), it calls sleep, it calls wait, it calls yield, it is preempted by a thread of higher priority or its quantum expires. A thread with a higher priority than the running thread can become ready (and hence preempt the running thread) if a sleeping thread finishes sleeping, if I/O completes for a thread waiting for that I/O or if either notify or notifyAll is called on a thread that has called wait.

844

Multithreading

Chapter 15

 

 

 

Ready threads

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 10

 

 

 

A

 

 

B

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 9

 

 

 

C

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 8

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 7

 

 

 

D

 

 

E

 

 

 

F

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 6

 

 

 

G

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 5

 

 

 

H

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 2

 

 

 

J

 

 

K

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Priority 1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Fig. 15.2 Java thread priority scheduling.

The application of Fig. 15.3 demonstrates basic threading techniques, including creation of a class derived from Thread, construction of a Thread and using the Thread class sleep method. Each thread of execution we create in the program displays its name after sleeping for a random amount of time between 0 and 5 seconds. You will see that the main method (i.e., the main thread of execution) terminates before the application terminates. The program consists of two classes—ThreadTester (lines 4–28) and PrintThread (lines 33–71).

Class PrintThread inherits from Thread, so that each object of the class can execute in parallel. The class consists of instance variable sleepTime, a constructor and a

Chapter 15

Multithreading

845

run method. Variable sleepTime stores a random integer value chosen when the program creates a PrintThread object. Each PrintThread object sleeps for the amount of time specified by its sleepTime, then outputs its name.

The PrintThread constructor (lines 38–48) initializes sleepTime to a random integer between 0 and 4999 (0 to 4.999 seconds). Then, the constructor outputs the name of the thread and the value of sleepTime to show the values for the particular PrintThread being constructed. The name of each thread is specified as a String argument to the PrintThread constructor and is passed to the superclass constructor at line 40. [Note: It is possible to allow class Thread to choose a name for your thread by using the Thread class’s default constructor.

1// Fig. 15.3: ThreadTester.java

2 // Show multiple threads printing at different intervals.

3

4 public class ThreadTester {

5

6// create and start threads

7public static void main( String args[] )

8{

9 PrintThread thread1, thread2, thread3, thread4;

10

11// create four PrintThread objects

12thread1 = new PrintThread( "thread1" );

13thread2 = new PrintThread( "thread2" );

14thread3 = new PrintThread( "thread3" );

15thread4 = new PrintThread( "thread4" );

17 System.err.println( "\nStarting threads" );

18

19// start executing PrintThreads

20thread1.start();

21thread2.start();

22thread3.start();

23thread4.start();

24

25System.err.println( "Threads started\n" );

26}

27

28 } // end class ThreadTester

29

30// Each object of this class picks a random sleep interval.

31// When a PrintThread executes, it prints its name, sleeps,

32// prints its name again and terminates.

33class PrintThread extends Thread {

34private int sleepTime;

35

36// PrintThread constructor assigns name to thread

37// by calling superclass Thread constructor

Fig. 15.3 Multiple threads printing at random intervals (part 1 of 3).

846

Multithreading

Chapter 15

38public PrintThread( String name )

39{

40super( name );

41

42// sleep between 0 and 5 seconds

43sleepTime = (int) ( Math.random() * 5000 );

45// display name and sleepTime

46System.err.println(

47"Name: " + getName() + "; sleep: " + sleepTime );

48}

49

50// control thread's execution

51public void run()

52{

53// put thread to sleep for a random interval

54try {

55

System.err.println( getName() + " going to sleep" );

56

 

57

// put thread to sleep

58Thread.sleep( sleepTime );

59}

60

61// if thread interrupted during sleep, catch exception

62// and display error message

63catch ( InterruptedException interruptedException ) {

64System.err.println( interruptedException.toString() );

65}

66

67// print thread name

68System.err.println( getName() + " done sleeping" );

69}

70

71 } // end class PrintThread

Name: thread1; sleep: 3593

Name: thread2; sleep: 2653

Name: thread3; sleep: 4465

Name: thread4; sleep: 1318

Starting threads

Threads started

thread1 going to sleep thread2 going to sleep thread3 going to sleep thread4 going to sleep thread4 done sleeping thread2 done sleeping thread1 done sleeping thread3 done sleeping

Fig. 15.3 Multiple threads printing at random intervals (part 2 of 3).

Chapter 15

Multithreading

847

Name: thread1; sleep: 2753

Name: thread2; sleep: 3199

Name: thread3; sleep: 2797

Name: thread4; sleep: 4639

Starting threads

Threads started

thread1 going to sleep thread2 going to sleep thread3 going to sleep thread4 going to sleep thread1 done sleeping thread3 done sleeping thread2 done sleeping thread4 done sleeping

Fig. 15.3 Multiple threads printing at random intervals (part 3 of 3).

When the program invokes a PrintThread’s start method (inherited from Thread), the PrintThread object enters the ready state. When the system assigns a processor to the PrintThread object, it enters the running state and its run method begins execution. Method run (lines 51–69) prints a String in the command window indicating that the thread is going to sleep (line 55), then invokes the sleep method (line 58) to place the thread into a sleeping state. At this point, the thread loses the processor, and the system allows another thread to execute. When the thread awakens, it is placed in a ready state again until the system assigns to the thread. When the PrintThread object enters the running state again, line 68 outputs the thread’s name (indicating that the thread is done sleeping), the run method terminates and the thread object enters the dead state. Note that method sleep can throw a checked InterruptedException (if another thread invokes the sleeping thread’s interrupt method); therefore, sleep must be called in a try block (in this example, we simply output the String representation of the exception if one occurs). Note that this example uses System.err rather than System.out to output lines of text. System.err represents the standard error object, which outputs error messages (normally to the command window). System.out performs buffered output—it is possible that a message output with System.out will not be output immediately. On the other hand, System.err uses unbuffered output—messages appear immediately when they are output. Using System.err in a multithreaded program helps ensure that the messages from our program are output in the correct order.

Class ThreadTester’s main method (lines 7–26) creates four objects of class PrintThread (lines 12–15) and invokes the Thread class start method on each one (lines 20–23) to place all four PrintThread objects in a ready state. Note that the program terminates execution when the last PrintThread awakens and prints its name. Also, note that the main method (i.e., the main thread of execution) terminates after starting the four PrintThreads, but the application does not terminate until the last thread dies.