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.

上一篇教程向您展示了如何在一个独立的线程中执行一个任务。你大多数情况下,只想运行一次这个任务。如果你想要在不同的数据集上重复运行一个任务,但是在应用运行的时候,你需要执行一次,那么,IntentService是你不错的选择。为了当资源变得可用时以自动运行任务,或者允许多个任务同时运行,你需要提供一个受到管理的线程集。为了做到这一点,使用ThreadPoolExecutor的实例吧,它允许它的线程池中一个线程变得可用时,从一个队列中取出一个任务,然后交给这个可用的线程去执行。为了运行一个任务,你需要做的就是将这个任务添加到队列中。似乎一个线程池有一个任务队列,不断地往队列中添加任务,当线程池中的线程可用时,从队列中取出任务,在这个线程上执行。自己创建一个线程池,并且维护一个队列。

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 asynchronized 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


Instantiate ThreadPoolExecutor in its own class. Within this class, do the following:

初始化ThreadPoolExecutor,要做的事情有以下几个:

Use static variables for thread pools
将线程池声明为静态变量
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:
为了对有限的CPU资源或者网络资源有一个单独的控制点,你也许需要为你的应用设置一个线程池的单例,如果你有不同的runnable类型,那么你也许想要为每个这样的类型搞一个线程池,比如,你可以加入作为你的全局域的声明:不同的runnable类型是指由下载任务,有解码任务等。每种类型的任务都可以有自己的线程池来执行。
public class PhotoManager {
    ...
    static  {
        ...
        // Creates a single static instance of PhotoManager
        sInstance = new PhotoManager();
    }
    ...

Use a private constructor

使用一个私有的构造器

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());
        ...
    }

  

整个应用通过sInstance这一个单例模式,来调用mDownloadThreadPool这个下载线程池,并且调用execute方法来添加一个下载任务到下载线程池的工作队列。threadpooledecuter可以看成是一个线程池,executer方法就是添加任务到队列。

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 lessonCommunicate with the UI Thread. For example:

在构造器中初始化一个句柄,并且将这个句柄附加到你的主线程中。一个句柄允许你的应用能够安全调用UI对象的方法,比如View对象。只有UI线程才能改变大部分的UI对象。例如:

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) {
                ...
            }
        ...
        }
    }

  

Determine the Thread Pool Parameters


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

一旦你对类的结构有一个大体的了解,你可以开始定义线程池了。为了初始化一个ThreadPoolExecutor对象你需要下列的值:

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.

这个数字也许与实际的核数有出入。一些设备的CPU会根据系统的负载来熄灭一个或者几个核。对于这些设备,availableProcessors方法只是返回激活的核数,可能小于本应该有的核数。也就是说,能够为线程池分配多少个线程,依赖于设备的核数,而设备的核数可以通过availableProcessors方法来获取,这个方法获取的仅仅是激活的核数,而不是所有的核数。

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.

在一个线程结束前的空闲时间时长。这个时长由time unit值来解释,这个时长在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 forThreadPoolExecutor. This example uses the LinkedBlockingQueue class:

一个队列,在该队列中,ThreadPoolExecutor取回runnable对象。为了启动一个线程中的代码,线程池管理者在队列中取回一个runnable对象给线程。当你创建了线程池时,你是使用的实现了BlockingQueue接口的队列类来提供一个队列。为了满足你的应用的需求,你可以使用可用的队列实现,似乎是系统已经做好的。这个例子使用了LinkedBlockingQueue类。该类实现了BlockingQueue接口。

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>();
        ...
    }
    ...
}

  

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:

为了创建线程池,调用ThreadPoolExecutor方法来初始化一个线程池管理者。这个将会创建和管理一个大小受限的线程组。由于初始化的池大小和最大的大小事相同的,因此当池子初始化完毕后,ThreadPoolExecutor就已经穿件好了所有的线程。

 

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);
    }

 

  

 

posted on 2013-03-03 21:21  C语言答疑课堂  阅读(264)  评论(0编辑  收藏  举报