Creating and Executing Threads

In J2SE 5.0, the preferred means of creating a multithreaded application is to implement the Runnable interface (package java.lang) and use built-in methods and classes to create Threads that execute the Runnables. The Runnable interface declares a single method named run. Runnables are executed by an object of a class that implements the Executor interface (package java.util.concurrent). This interface declares a single method named execute. An Executor object typically creates and manages a group of threads called a thread pool. These threads execute the Runnable objects passed to the execute method. The Executor assigns each Runnable to one of the available threads in the thread pool. If there are no available threads in the thread pool, the Executor creates a new thread or waits for a thread to become available and assigns that thread the Runnable that was passed to method execute. Depending on the Executor type, there may be a limit to the number of threads that can be created. Interface ExecutorService (package java.util.concurrent) is a subinterface of Executor that declares a number of other methods for managing the life cycle of the Executor. An object that implements the ExecutorService interface can be created using static methods declared in class Executors (package java.util.concurrent). We use these interfaces and methods in the next application, which executes three threads.

Class PrintTask (Fig. 23.4) implements Runnable (line 5), so that each PrintTask object can execute concurrently. Variable sleepTime (line 7) stores a random integer value (line 17) chosen when the PrintTask constructor executes. Each thread running a PrintTask object sleeps for the amount of time specified by the corresponding PrintTask object's sleepTime, then outputs its name.

Figure 23.4. Threads sleeping and awakening.

(This item is displayed on page 1060 in the print version)

 1 // Fig. 23.4: PrintTask.java
 2 // PrintTask class sleeps for a random time from 0 to 5 seconds
 3 import java.util.Random;
 4
 5 class PrintTask implements Runnable
 6 {
 7 private int sleepTime; // random sleep time for thread
 8 private String threadName; // name of thread
 9 private static Random generator = new Random();
10
11 // assign name to thread
12 public PrintTask( String name )
13 {
14 threadName = name; // set name of thread
15
16 // pick random sleep time between 0 and 5 seconds
17 sleepTime = generator.nextInt( 5000 );
18 } // end PrintTask constructor
19
20 // method run is the code to be executed by new thread
21 public void run() 
22 {
23 try // put thread to sleep for sleepTime amount of time
24 {
25 System.out.printf( "%s going to sleep for %d milliseconds.
",
26 threadName, sleepTime );
27
28 Thread.sleep( sleepTime ); // put thread to sleep
29 } // end try
30 // if thread interrupted while sleeping, print stack trace
31 catch ( InterruptedException exception )
32 {
33 exception.printStackTrace();
34 } // end catch
35
36 // print thread name
37 System.out.printf( "%s done sleeping
", threadName );
38 } // end method run
39 } // end class PrintTask

When a PrintTask is assigned to a processor for the first time, its run method (lines 2138) begins execution. Lines 2526 display a message indicating the name of the currently executing thread and stating that the thread is going to sleep for a certain number of milliseconds. Line 26 uses the tHReadName field which was initialized at line 14 with the PrintTask constructor's argument. Line 28 invokes static method sleep of class THRead to place the thread into the timed waiting state. At this point, the thread loses the processor, and the system allows another thread to execute. When the thread awakens, it reenters the runnable state. When the PrintTask is assigned to a processor again, line 37 outputs the thread's name in a message that indicates the thread is done sleepingthen method run terminates. Note that the catch at lines 3134 is required because method sleep might throw an InterruptedException, which is a checked exception. Such an exception occurs if a sleeping thread's interrupt method is called. Most programmers do not directly manipulate Thread objects, so InterruptedExceptions are unlikely to occur.

Figure 23.5 creates three threads of execution using the PrintTask class. Method main (lines 828) creates and names three PrintTask objects (lines 1113). Line 18 creates a new ExecutorService. This line uses the newFixedThreadPool method of class Executors, which creates a pool consisting of a fixed number of Threads as indicated by the method's argument (in this case, 3). These Threads are used by threadExecutor to execute the Runnables. If method execute is called and all the threads in the ExecutorService are being used, the Runnable will be placed in a queue and assigned to the first thread that completes its previous task. Executors method newCachedThreadPool returns an ExecutorService that creates new threads as they are needed by the application.

Figure 23.5. Creates three PrintTasks and executes them.

(This item is displayed on page 1061 in the print version)

 1 // Fig. 23.5: RunnableTester.java
 2 // Multiple threads printing at different intervals.
 3 import java.util.concurrent.Executors;
 4 import java.util.concurrent.ExecutorService;
 5
 6 public class RunnableTester
 7 {
 8 public static void main( String[] args )
 9 {
10 // create and name each runnable 
11 PrintTask task1 = new PrintTask( "thread1" );
12 PrintTask task2 = new PrintTask( "thread2" );
13 PrintTask task3 = new PrintTask( "thread3" );
14
15 System.out.println( "Starting threads" );
16
17 // create ExecutorService to manage threads 
18 ExecutorService threadExecutor = Executors.newFixedThreadPool( 3 );
19
20 // start threads and place in runnable state 
21 threadExecutor.execute( task1 ); // start task1
22 threadExecutor.execute( task2 ); // start task2
23 threadExecutor.execute( task3 ); // start task3
24
25 threadExecutor.shutdown(); // shutdown worker threads
26
27 System.out.println( "Threads started, main ends
" );
28 } // end main
29 } // end class RunnableTester
 
Starting threads
Threads started, main ends

thread1 going to sleep for 1217 milliseconds
thread2 going to sleep for 3989 milliseconds
thread3 going to sleep for 662 milliseconds
thread3 done sleeping
thread1 done sleeping
thread2 done sleeping
 
 
Starting threads
thread1 going to sleep for 314 milliseconds
thread2 going to sleep for 1990 milliseconds
Threads started, main ends

thread3 going to sleep for 3016 milliseconds
thread1 done sleeping
thread2 done sleeping
thread3 done sleeping
 

Lines 2123 invoke the ExecutorService's execute method. This method creates a new THRead inside the ExecutorService to run the Runnable passed to it as an argument (in this case a PrintTask) and transitions that Thread from the new state to the runnable state. Method execute returns immediately from each invocationthe program does not wait for each PrintTask to finish. Line 25 calls ExecutorService method shutdown, which will end each Thread in threadExecutor as soon as each finishes executing its Runnable. Line 27 outputs a message indicating that the threads were started. [Note: Line 18 creates the ExecutorService using method newFixedThreadPool and the argument 3. This program executes only three Runnables, so a new THRead will be created by the ExecutorService for each Runnable. If the program executed more than three Runnables, additional Threads would not be created, but rather an existing Thread would be reused when it completed the Runnable assigned to it.]

The code in method main executes in the main thread. This thread is created by the JVM and executes the main method. The code in the run method of PrintTask (lines 2138 of Fig. 23.4) executes in the threads created by the ExecutorService. When method main terminates (line 28), the program itself continues running because there are still threads that are alive (i.e., the threads started by tHReadExecutor that have not yet reached the terminated state). The program will not terminate until its last thread completes execution.

The sample outputs for this program show each thread's name and sleep time as the thread goes to sleep. The thread with the shortest sleep time normally awakens first, indicates that it is done sleeping and terminates. In Section 23.8, we discuss multithreading issues that could prevent the thread with the shortest sleep time from awakening first. In the first output, the main thread terminates before any of the other threads output their names and sleep times. This shows that the main thread runs to completion before any of the other threads get a chance to run. In the second output, the first two threads output their names and sleep times before the main thread terminates. This shows that the operating system allowed other threads to execute before the main thread terminated. This is an example of the round-robin scheduling we discussed in Section 23.3.


Introduction to Computers, the Internet and the World Wide Web

Introduction to Java Applications

Introduction to Classes and Objects

Control Statements: Part I

Control Statements: Part 2

Methods: A Deeper Look

Arrays

Classes and Objects: A Deeper Look

Object-Oriented Programming: Inheritance

Object-Oriented Programming: Polymorphism

GUI Components: Part 1

Graphics and Java 2D™

Exception Handling

Files and Streams

Recursion

Searching and Sorting

Data Structures

Generics

Collections

Introduction to Java Applets

Multimedia: Applets and Applications

GUI Components: Part 2

Multithreading

Networking

Accessing Databases with JDBC

Servlets

JavaServer Pages (JSP)

Formatted Output

Strings, Characters and Regular Expressions

Appendix A. Operator Precedence Chart

Appendix B. ASCII Character Set

Appendix C. Keywords and Reserved Words

Appendix D. Primitive Types

Appendix E. (On CD) Number Systems

Appendix F. (On CD) Unicode®

Appendix G. Using the Java API Documentation

Appendix H. (On CD) Creating Documentation with javadoc

Appendix I. (On CD) Bit Manipulation

Appendix J. (On CD) ATM Case Study Code

Appendix K. (On CD) Labeled break and continue Statements

Appendix L. (On CD) UML 2: Additional Diagram Types

Appendix M. (On CD) Design Patterns

Appendix N. Using the Debugger

Inside Back Cover



Java(c) How to Program
Java How to Program (6th Edition) (How to Program (Deitel))
ISBN: 0131483986
EAN: 2147483647
Year: 2003
Pages: 615

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