Java Reference
In-Depth Information
Lines 21-23 each invoke the ExecutorService 's execute method, which executes its
Runnable argument (in this case a PrintTask ) some time in the future. The specified task
may execute in one of the threads in the ExecutorService 's thread pool, in a new thread
created to execute it, or in the thread that called the execute method—the ExecutorSer-
vice manages these details. Method execute returns immediately from each invocation—
the program does not wait for each PrintTask to finish. Line 26 calls ExecutorService
method shutdown , which notifies the ExecutorService to stop accepting new tasks, but con-
tinues executing tasks that have already been submitted . Once all of the previously submitted
Runnable s have completed, the ExecutorService terminates. Line 28 outputs a message
indicating that the tasks were started and the main thread is finishing its execution.
Main Thread
The code in main executes in the main thread , which is created by the JVM. The code in
the run method of PrintTask (lines 21-37 of Fig. 23.3) executes whenever the Executor
starts each PrintTask —again, this is sometime after they're passed to the ExecutorSer-
vice 's execute method (Fig. 23.4, lines 21-23). When main terminates, the program it-
self continues running because there are still tasks that must finish executing. The program
will not terminate until these tasks complete.
Sample Outputs
The sample outputs show each task's name and sleep time as the thread goes to sleep. The
thread with the shortest sleep time in most cases awakens first, indicates that it's done sleep-
ing 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 PrintTask s output their names and sleep times. This
shows that the main thread runs to completion before any of the PrintTask s gets a chance
to run. In the second output, all of the PrintTask s output their names and sleep times
before the main thread terminates. This shows that the PrintTask s started executing before
the main thread terminated. Also, notice in the second example output, task3 goes to
sleep before task2 last, even though we passed task2 to the ExecutorService 's execute
method before task3 . This illustrates the fact that we cannot predict the order in which the
tasks will start executing, even if we know the order in which they were created and started .
Waiting for Previously Scheduled Tasks to Terminate
After scheduling tasks to execute, you'll typically want to wait for the tasks to complete —for
example, so that you can use the tasks' results. After calling method shutdown , you can call
ExecutorService method awaitTermination to wait for scheduled tasks to complete. We
demonstrate this in Fig. 23.7. We purposely did not call awaitTermination in Fig. 23.4
to demonstrate that a program can continue executing after the main thread terminates.
23.4 Thread Synchronization
When multiple threads share an object and it's modified by one or more of them, indeter-
minate results may occur (as we'll see in the examples) unless access to the shared object is
managed properly. If one thread is in the process of updating a shared object and another
thread also tries to update it, it's uncertain which thread's update takes effect. Similarly, if
one thread is in the process of updating a shared object and another thread tries to read it,
it's uncertain whether the reading thread will see the old value or the new one. In such
 
 
Search WWH ::




Custom Search