239 lines
8.9 KiB
Plaintext
239 lines
8.9 KiB
Plaintext
|
page.title=Creating a Manager for Multiple Threads
|
||
|
|
||
|
trainingnavtop=true
|
||
|
@jd:body
|
||
|
|
||
|
<div id="tb-wrapper">
|
||
|
<div id="tb">
|
||
|
|
||
|
<!-- table of contents -->
|
||
|
<h2>This lesson teaches you to</h2>
|
||
|
<ol>
|
||
|
<li><a href="#ClassStructure">Define the Thread Pool Class</a>
|
||
|
<li><a href="#PoolParameters">Determine the Thread Pool Parameters</a></li>
|
||
|
<li><a href="#ThreadPool">Create a Pool of Threads</a></li>
|
||
|
</ol>
|
||
|
|
||
|
<!-- other docs (NOT javadocs) -->
|
||
|
<h2>You should also read</h2>
|
||
|
<ul>
|
||
|
<li><a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a></li>
|
||
|
</ul>
|
||
|
|
||
|
<h2>Try it out</h2>
|
||
|
<div class="download-box">
|
||
|
<a href="{@docRoot}shareables/training/ThreadSample.zip" class="button">Download the sample</a>
|
||
|
<p class="filename">ThreadSample.zip</p>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<p>
|
||
|
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 {@link android.app.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
|
||
|
{@link java.util.concurrent.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.
|
||
|
</p>
|
||
|
<p>
|
||
|
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
|
||
|
<code>synchronized</code> 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
|
||
|
<a href="{@docRoot}http://developer.android.com/guide/components/processes-and-threads.html">
|
||
|
Processes and Threads</a> API guide.
|
||
|
|
||
|
</p>
|
||
|
<h2 id="ClassStructure">Define the Thread Pool Class</h2>
|
||
|
<p>
|
||
|
Instantiate {@link java.util.concurrent.ThreadPoolExecutor} in its own class. Within this class,
|
||
|
do the following:
|
||
|
</p>
|
||
|
<dl>
|
||
|
<dt>
|
||
|
Use static variables for thread pools
|
||
|
</dt>
|
||
|
<dd>
|
||
|
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
|
||
|
{@link java.lang.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:
|
||
|
<pre>
|
||
|
public class PhotoManager {
|
||
|
...
|
||
|
static {
|
||
|
...
|
||
|
// Creates a single static instance of PhotoManager
|
||
|
sInstance = new PhotoManager();
|
||
|
}
|
||
|
...
|
||
|
</pre>
|
||
|
</dd>
|
||
|
<dt>
|
||
|
Use a private constructor
|
||
|
</dt>
|
||
|
<dd>
|
||
|
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 <code>synchronized</code> block:
|
||
|
<pre>
|
||
|
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() {
|
||
|
...
|
||
|
}
|
||
|
</pre>
|
||
|
</dd>
|
||
|
<dt>
|
||
|
Start your tasks by calling methods in the thread pool class.
|
||
|
</dt>
|
||
|
<dd>
|
||
|
Define a method in the thread pool class that adds a task to a thread pool's queue. For
|
||
|
example:
|
||
|
<pre>
|
||
|
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());
|
||
|
...
|
||
|
}
|
||
|
</pre>
|
||
|
</dd>
|
||
|
<dt>
|
||
|
Instantiate a {@link android.os.Handler} in the constructor and attach it to your app's
|
||
|
UI thread.
|
||
|
</dt>
|
||
|
<dd>
|
||
|
A {@link android.os.Handler} allows your app to safely call the methods of UI objects
|
||
|
such as {@link android.view.View} objects. Most UI objects may only be safely altered from
|
||
|
the UI thread. This approach is described in more detail in the lesson
|
||
|
<a href="communicate-ui.html">Communicate with the UI Thread</a>. For example:
|
||
|
<pre>
|
||
|
private PhotoManager() {
|
||
|
...
|
||
|
// Defines a Handler object that's attached to the UI thread
|
||
|
mHandler = new Handler(Looper.getMainLooper()) {
|
||
|
/*
|
||
|
* handleMessage() defines the operations to perform when
|
||
|
* the Handler receives a new Message to process.
|
||
|
*/
|
||
|
@Override
|
||
|
public void handleMessage(Message inputMessage) {
|
||
|
...
|
||
|
}
|
||
|
...
|
||
|
}
|
||
|
}
|
||
|
</pre>
|
||
|
</dd>
|
||
|
</dl>
|
||
|
<h2 id="PoolParameters">Determine the Thread Pool Parameters</h2>
|
||
|
<p>
|
||
|
Once you have the overall class structure, you can start defining the thread pool. To
|
||
|
instantiate a {@link java.util.concurrent.ThreadPoolExecutor} object, you need the
|
||
|
following values:
|
||
|
</p>
|
||
|
<dl>
|
||
|
<dt>
|
||
|
Initial pool size and maximum pool size
|
||
|
</dt>
|
||
|
<dd>
|
||
|
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:
|
||
|
<pre>
|
||
|
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();
|
||
|
}
|
||
|
</pre>
|
||
|
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,
|
||
|
{@link java.lang.Runtime#availableProcessors availableProcessors()} returns the number of
|
||
|
<i>active</i> cores, which may be less than the total number of cores.
|
||
|
</dd>
|
||
|
<dt>
|
||
|
Keep alive time and time unit
|
||
|
</dt>
|
||
|
<dd>
|
||
|
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
|
||
|
{@link java.util.concurrent.TimeUnit}.
|
||
|
</dd>
|
||
|
<dt>
|
||
|
A queue of tasks
|
||
|
</dt>
|
||
|
<dd>
|
||
|
The incoming queue from which {@link java.util.concurrent.ThreadPoolExecutor} takes
|
||
|
{@link java.lang.Runnable} objects. To start code on a thread, a thread pool manager takes a
|
||
|
{@link java.lang.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 {@link java.util.concurrent.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 {@link java.util.concurrent.ThreadPoolExecutor}.
|
||
|
This example uses the {@link java.util.concurrent.LinkedBlockingQueue} class:
|
||
|
<pre>
|
||
|
public class PhotoManager {
|
||
|
...
|
||
|
private PhotoManager() {
|
||
|
...
|
||
|
// A queue of Runnables
|
||
|
private final BlockingQueue<Runnable> mDecodeWorkQueue;
|
||
|
...
|
||
|
// Instantiates the queue of Runnables as a LinkedBlockingQueue
|
||
|
mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
|
||
|
...
|
||
|
}
|
||
|
...
|
||
|
}
|
||
|
</pre>
|
||
|
</dd>
|
||
|
</dl>
|
||
|
<h2 id="ThreadPool">Create a Pool of Threads</h2>
|
||
|
<p>
|
||
|
To create a pool of threads, instantiate a thread pool manager by calling
|
||
|
{@link java.util.concurrent.ThreadPoolExecutor#ThreadPoolExecutor ThreadPoolExecutor()}.
|
||
|
This creates and manages a constrained group of threads. Because the initial pool size and
|
||
|
the maximum pool size are the same, {@link java.util.concurrent.ThreadPoolExecutor} creates
|
||
|
all of the thread objects when it is instantiated. For example:
|
||
|
</p>
|
||
|
<pre>
|
||
|
private PhotoManager() {
|
||
|
...
|
||
|
// Sets the amount of time an idle thread waits before terminating
|
||
|
private static final int KEEP_ALIVE_TIME = 1;
|
||
|
// Sets the Time Unit to seconds
|
||
|
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
|
||
|
// Creates a thread pool manager
|
||
|
mDecodeThreadPool = new ThreadPoolExecutor(
|
||
|
NUMBER_OF_CORES, // Initial pool size
|
||
|
NUMBER_OF_CORES, // Max pool size
|
||
|
KEEP_ALIVE_TIME,
|
||
|
KEEP_ALIVE_TIME_UNIT,
|
||
|
mDecodeWorkQueue);
|
||
|
}
|
||
|
</pre>
|