- Forward


Executors and Thread Pools
An Introduction with Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu

Print

Background
Back SMYC Forward
  • Using Multiple Threads:
    • Allows a program to do multiple things "at the same time"
  • A Common "Mistake":
    • Coupling the task to be performed with how the task will be executed
  • A Common Defect Leading to this "Mistake":
    • A performance defect that is corrected by adding a thread
Performance Defects
Back SMYC Forward
  • Defined:
    • Code that takes too much time to execute
  • Common Types:
    • Complicated code
    • Code that blocks/sleeps/waits
Performance Defects (cont.)
Back SMYC Forward
  • An Example Revisited:
    • The sendMessage() method in the Dispatcher is being executed in the caller's thread of execution
  • The Defect:
    • This method won't return until there are vehicles available which could be a problem for both the RealTimeDispatchHandler and the DailyDispatchHandler
Performance Defects (cont.)
Back SMYC Forward
  • Going Further with this Example:
    • The actual sending of the message (e.g., using the Internet) may take time
  • The Defect:
    • Only one vehicle can be dispatched "at a time"
"Fixing" these Defects
Back SMYC Forward
  • "Fixing" One Aspect:
    • Have sendMessage() add tasks to a queue and return "immediately"
  • "Fixing" the Other Aspect:
    • Use a thread for each task in the queue
  • The "Mistake":
    • Coupling the task to be performed with the way it is executed
Encouraging Decoupling
Back SMYC Forward
  • The Interface:
    • Executor
  • Methods:
    • execute(Runnable task)
  • How It's Used:
    1. Construct an Executor
    2. Construct one or more Runnable objects that encapsulate the task(s) to be performed
    3. Call execute()
Constructing Executor Objects
Back SMYC Forward
  • The Issue:
    • Constructing objects that implement these interfaces can be complicated so use a factory
  • The Class:
    • Executors
  • Some Methods:
    • newCachedThreadPool()
    • newFixedThreadPool(n)
    • newSchedueldThreadPool(n)
    • newSingleThreadExecutor()
    • newSingleThreadExecutorService()
Termination
Back SMYC Forward
  • A Complication of Decoupling:
    • The termination of tasks and waiting for tasks to terminate needs to be handled by the executor (i.e., since we don't know what Thread is being used, we can't` call its interrupt() or join() methods)
  • The Solution:
    • Add capabilities by specializing the Executor interface (i.e., create a subinterface)
Termination (cont.)
Back SMYC Forward
  • The Subinterface:
    • ExecutorService
  • Methods:
    • submit(task)
    • awaitTermination(time, timeunit)
    • shutdown()
    • shutdownNow()
    • isShutdown()
    • isTerminated()
Orderly Shutdown
Back SMYC Forward
try { executor.shutdown(); executor.awaitTermination(WAIT, TimeUnit.SECONDS); } catch (InterruptedException ie) { } finally { if (!executor.isTerminated()) { } executor.shutdownNow(); }
Repeated Tasks
Back SMYC Forward
  • The Situation:
    • We want to perform the same task more than once
  • Approaches:
    • Have the task itself handle the repetition
    • Execute a "one-shot" task multiple times
Repeated Tasks (cont.)
Back SMYC Forward

A Repeating Task

javaexamples/threads_executor/v1/RepeatingDirectoryLister.java
 
Repeated Tasks (cont.)
Back SMYC Forward

The Main Class

javaexamples/threads_executor/v1/AutoDir.java
 
Repeated Tasks (cont.)
Back SMYC Forward
  • What's Needed for the Decoupled Approach?
    • An ExecutorService that can repeatedly execute a task
  • The Subinterface:
    • ScheduledExecutorService
  • Methods:
    • schedule(task, delay, timunit)
    • scheduleAtFixedRate(task, initialDelay, period, timeunit)
    • scheduleWithFixedDelay(task, initialDelay, delay, timunit)
Repeated Tasks (cont.)
Back SMYC Forward

A "One-Shot" Task

javaexamples/threads_executor/v2/DirectoryLister.java
 
Repeated Tasks (cont.)
Back SMYC Forward

The Main Class using a ScheduledExecutorService

javaexamples/threads_executor/v2/AutoDir.java
 
Return Values from Tasks
Back SMYC Forward
  • A Limitation:
    • Since Runnable is void, the tasks can't return anything (except indirectly through reference parameters that are passed in)
  • An Easy Fix Ignoring Threads:
    • The Callable interface has a call() method that returns a parameterized type
  • The Complication of Threads:
    • In a multi-threaded application the task will be performed asynchronously so the result won't be immediately available
Return Values from Tasks (cont.)
Back SMYC Forward
  • The Interface:
    • Future
  • Purpose:
    • Represents the result of an asynchronous task
  • Methods:
    • get() - wait for the task to complete (perhaps for a maximum amount of time)
    • isDone() - determine if the task is complete
    • cancel() - attempt to cancel the task
    • isCancelled() - determine if the task was cancelled before completion
  • An Observation:
    • A Future can be used like a latch
Return Values (cont.)
Back SMYC Forward

A Revised "One-Shot" Task

javaexamples/threads_executor/v3/DirectoryLister.java
 
Return Values (cont.)
Back SMYC Forward

Using the Return Value

javaexamples/threads_executor/v3/AutoDir.java
 
Cancelling Tasks with a Future
Back SMYC Forward
  • An Observation:
    • A Future object can be useful even when the task does not return anything since it provides other functionality
  • Our Current Interest:
    • A Future object has a cancel() method
  • A Detail:
    • The Future object can be parameterized using ? to indicate that we don't care about the return type
Cancelling Tasks (cont.)
Back SMYC Forward

A Terminator Task

javaexamples/threads_executor/v4/Terminator.java
 
Cancelling Tasks (cont.)
Back SMYC Forward

Scheduling a Terminator Task

javaexamples/threads_executor/v4/AutoDir.java
 
Happens-Before
Back SMYC Forward
  • Executor and ExecutorService:
    • Actions in a thread prior to the submission of a Runnable to an Executor happen-before its execution begins
    • Actions in a thread prior to the submission of a Callable to an ExecutorService happen-before its execution begins
  • Future:
    • Actions taken by the asynchronous computation represented by a Future happen-before actions subsequent to the retrieval of the result via Future.get() in another thread
Related Topics
Back SMYC Forward
  • Executors:
    • newWorkStealingPool()
  • Fork-Join Computation:
    • ForkJoinTask
    • ForkJoinWorkerThread
    • ForkJoinPool
  • Graphical User Interfaces:
    • SwingWorker
There's Always More to Learn
Back -