This lesson teaches you to

You should also read

Try it out

The previous lesson showed how to define a task that executes on a
separate thread. If you only want to run the task once, this may be all you need. If you want
to run a task repeatedly on different sets of data, but you only need one execution running at a
time, an IntentService suits your needs. To automatically run tasks
as resources become available, or to allow multiple tasks to run at the same time (or both),
you need to provide a managed collection of threads. To do this, use an instance of
ThreadPoolExecutor, which runs a task from a queue when a thread
in its pool becomes free. To run a task, all you have to do is add it to the queue.

A thread pool can run multiple parallel instances of a task, so you should ensure that your
code is thread-safe. Enclose variables that can be accessed by more than one thread in a
synchronized block. This approach will prevent one thread from reading the variable
while another is writing to it. Typically, this situation arises with static variables, but it
also occurs in any object that is only instantiated once. To learn more about this, read the
Processes and Threads API guide.

Define the Thread Pool Class

You may only want a single instance of a thread pool for your app, in order to have a
single control point for restricted CPU or network resources. If you have different
Runnable types, you may want to have a thread pool for each one, but each
of these can be a single instance. For example, you can add this as part of your
global field declarations:

Making the constructor private ensures that it is a singleton, which means that you don't
have to enclose accesses to the class in a synchronized block:

public class PhotoManager {
...
/**
* Constructs the work queues and thread pools used to download
* and decode images. Because the constructor is marked private,
* it's unavailable to other classes, even in the same package.
*/
private PhotoManager() {
...
}

Start your tasks by calling methods in the thread pool class.

Define a method in the thread pool class that adds a task to a thread pool's queue. For
example:

public class PhotoManager {
...
// Called by the PhotoView to get a photo
static public PhotoTask startDownload(
PhotoView imageView,
boolean cacheFlag) {
...
// Adds a download task to the thread pool for execution
sInstance.
mDownloadThreadPool.
execute(downloadTask.getHTTPDownloadRunnable());
...
}

Instantiate a Handler in the constructor and attach it to your app's
UI thread.

A Handler allows your app to safely call the methods of UI objects
such as View objects. Most UI objects may only be safely altered from
the UI thread. This approach is described in more detail in the lesson
Communicate with the UI Thread. For example:

Determine the Thread Pool Parameters

Once you have the overall class structure, you can start defining the thread pool. To
instantiate a ThreadPoolExecutor object, you need the
following values:

Initial pool size and maximum pool size

The initial number of threads to allocate to the pool, and the maximum allowable number.
The number of threads you can have in a thread pool depends primarily on the number of cores
available for your device. This number is available from the system environment:

public class PhotoManager {
...
/*
* Gets the number of available cores
* (not always the same as the maximum number of cores)
*/
private static int NUMBER_OF_CORES =
Runtime.getRuntime().availableProcessors();
}

This number may not reflect the number of physical cores in the device; some devices have
CPUs that deactivate one or more cores depending on the system load. For these devices,
availableProcessors() returns the number of
active cores, which may be less than the total number of cores.

Keep alive time and time unit

The duration that a thread will remain idle before it shuts down. The duration is
interpreted by the time unit value, one of the constants defined in
TimeUnit.

A queue of tasks

The incoming queue from which ThreadPoolExecutor takes
Runnable objects. To start code on a thread, a thread pool manager takes a
Runnable object from a first-in, first-out queue and attaches it to the
thread. You provide this queue object when you create the thread pool, using any queue class
that implements the BlockingQueue interface. To match the
requirements of your app, you can choose from the available queue implementations; to learn
more about them, see the class overview for ThreadPoolExecutor.
This example uses the LinkedBlockingQueue class:

Create a Pool of Threads

To create a pool of threads, instantiate a thread pool manager by calling
ThreadPoolExecutor().
This creates and manages a constrained group of threads. Because the initial pool size and
the maximum pool size are the same, ThreadPoolExecutor creates
all of the thread objects when it is instantiated. For example: