欢迎参加我们将于 6 月 3 日举行的 #Android11:Beta 版发布会

在线程池中的线程上运行代码

上一篇指南介绍了如何为多个线程创建管理器,以及如何定义一个类来管理线程池以及在线程池上运行的任务。本课将介绍如何在线程池上运行任务。要实现此目的,您需要将任务添加到线程池的工作队列中。当有线程变得可用时,ThreadPoolExecutor 会从队列中获取任务并在该线程上运行该任务。

本课还将介绍如何停止正在运行的任务。如果某个任务启动后,您发现没有必要执行这项工作,您可能会希望停止该任务。您可以取消任务运行的线程,而不必浪费处理器时间。例如,如果您正在从网上下载图像并使用了缓存,则当任务检测到缓存中已存在该图像时,您可能会想要停止该任务。根据您编写应用的方式,您可能无法在开始下载之前检测到这一情况。

在线程池中的线程上运行任务

要在特定线程池中的线程上启动任务对象,请将 Runnable 传递给 ThreadPoolExecutor.execute()。此调用会将该任务添加到线程池的工作队列中。当有空闲线程变得可用时,管理器会接受等待时间最长的任务并在该线程上运行它:

Kotlin

    object PhotoManager {

        fun handleState(photoTask: PhotoTask, state: Int) {
            when (state) {
                DOWNLOAD_COMPLETE ->
                    // Decodes the image
                    decodeThreadPool.execute(photoTask.getPhotoDecodeRunnable())
                ...
            }
            ...
        }
        ...
    }
    

Java

    public class PhotoManager {
        public void handleState(PhotoTask photoTask, int state) {
            switch (state) {
                // The task finished downloading the image
                case DOWNLOAD_COMPLETE:
                // Decodes the image
                    decodeThreadPool.execute(
                            photoTask.getPhotoDecodeRunnable());
                ...
            }
            ...
        }
        ...
    }
    

ThreadPoolExecutor 在线程上启动 Runnable 时,它会自动调用对象的 run() 方法。

中断正在运行的代码

要停止任务,您需要中断任务的线程。要为执行此操作做准备,您需要在创建任务时存储任务线程的句柄。例如:

Kotlin

    class PhotoDecodeRunnable : Runnable {

        // Defines the code to run for this task
        override fun run() {
            /*
             * Stores the current Thread in the
             * object that contains PhotoDecodeRunnable
             */
            photoTask.setImageDecodeThread(Thread.currentThread())
            ...
        }
        ...
    }
    

Java

    class PhotoDecodeRunnable implements Runnable {
        // Defines the code to run for this task
        public void run() {
            /*
             * Stores the current Thread in the
             * object that contains PhotoDecodeRunnable
             */
            photoTask.setImageDecodeThread(Thread.currentThread());
            ...
        }
        ...
    }
    

要中断线程,请调用 Thread.interrupt()。请注意,Thread 对象由系统控制,系统可以在应用进程之外修改它们。因此,在中断线程之前您需要先锁定对它的访问,方法是将访问权限置于 synchronized 块中。例如:

Kotlin

    object PhotoManager {
        fun cancelAll() {
            /*
             * Creates and populates an array of Runnables with the Runnables in the queue
             */
            val runnableArray: Array<Runnable> = decodeWorkQueue.toTypedArray()
            /*
             * Iterates over the array of Runnables and interrupts each one's Thread.
             */
            synchronized(this) {
                // Iterates over the array of tasks
                runnableArray.map { (it as? PhotoDecodeRunnable)?.mThread }
                        .forEach { thread ->
                            thread?.interrupt()
                        }
            }
        }
        ...
    }
    

Java

    public class PhotoManager {
        public static void cancelAll() {
            /*
             * Creates an array of Runnables that's the same size as the
             * thread pool work queue
             */
            Runnable[] runnableArray = new Runnable[decodeWorkQueue.size()];
            // Populates the array with the Runnables in the queue
            mDecodeWorkQueue.toArray(runnableArray);
            // Stores the array length in order to iterate over the array
            int len = runnableArray.length;
            /*
             * Iterates over the array of Runnables and interrupts each one's Thread.
             */
            synchronized (sInstance) {
                // Iterates over the array of tasks
                for (int runnableIndex = 0; runnableIndex < len; runnableIndex++) {
                    // Gets the current thread
                    Runnable runnable = runnableArray[runnableIndex];
                    Thread thread = null;
                    if (runnable instanceof PhotoDecodeRunnable) {
                        thread = ((PhotoDecodeRunnable)runnable).mThread;
                    }
                    // if the Thread exists, post an interrupt to it
                    if (null != thread) {
                        thread.interrupt();
                    }
                }
            }
        }
        ...
    }
    

大多数情况下,Thread.interrupt() 会立即停止线程。但是,它只会停止等待中的线程,而不会中断 CPU 或网络密集型任务。为避免减慢或锁定系统,您应该在尝试操作之前先测试是否存在待处理的中断请求:

Kotlin

    /*
     * Before continuing, checks to see that the Thread hasn't
     * been interrupted
     */
    if (Thread.interrupted()) return
    ...
    // Decodes a byte array into a Bitmap (CPU-intensive)
    BitmapFactory.decodeByteArray(imageBuffer, 0, imageBuffer.size, bitmapOptions)
    ...
    

Java

    /*
     * Before continuing, checks to see that the Thread hasn't
     * been interrupted
     */
    if (Thread.interrupted()) {
        return;
    }
    ...
    // Decodes a byte array into a Bitmap (CPU-intensive)
    BitmapFactory.decodeByteArray(
            imageBuffer, 0, imageBuffer.length, bitmapOptions);
    ...
    

更多信息

要详细了解 Android 上的多线程操作,请参阅进程和线程概览指南。

应用示例

要试用本指南中的概念,请下载 ThreadSample