[译]Android 中的线程池使用

1,388 阅读6分钟
原文链接: ghui.me

这篇文章将会覆盖到线程池线程池Executor,以及它们在Android中的使用。通过大量的示例我们将完全覆盖这些主题。

本文是 Using ThreadPoolExecutor in Android 的翻译, 限于个人能力有限如有疑问请查看原文或留言.

线程池

一个线程池管理一池子的工作线程(工作线程的数量依赖于具体的线程池实现)。

一个任务队列容纳等待被线程池中的空闲线程执行的任务。任务被“生产者”添加到队列中,与之对应的工作线程作为消费者的角色消费任务队列中的任务,只要线程池中有空闲的线程等待去执行后台任务。

线程池Executor

线程池executor执行一个线程池中给定的任务。

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue
);

这些参数都是什么含义?

  1. corePoolSize: 线程池中保留的线程的最小数量。

    初始情况下线程池中的线程数量为0。但是随着任务被加到队列中,新的线程会被创建。如果在线程池中存在空闲的线程—但是线程的数量小于corePoolSize—那么新的线程会被继续创建。

  2. maximumPoolSize: 线程池中所能容纳的最大线程数量。如果它的数量大于corePoolSize—并且当前的线程数量>= corePoolSize—那么新的工作线程会被继续创建只要任务队列中的任务足够多。

  3. keepAliveTime: 当线程的数量大于corePoolSize,非核心的线程(额外的空闲线程)将会等待新的任务,如果它们在这个参数定义的时间内没有等到一个新的任务,这些线程会被终结。

  4. unit: 参数keepAliveTime的单位

  5. workQueue: 任务队列,它只接收runnable任务。它必须是一个BlockingQueue.

为何要使用线程池在Android或Java程序中

  1. 它是一个强大的任务处理框架,因为它支持额外的任务保存在队列中,支持任务取消,以及设置任务的优先级。
  2. 它减小了所需要创建的线程的最大数量,因为它维护了指定数量的线程在它的池子中。

在Android中使用ThreadPoolExecutor

首先,创建一个PriorityThreadFactory:

public class PriorityThreadFactory implements ThreadFactory {

    private final int mThreadPriority;

    public PriorityThreadFactory(int threadPriority) {
        mThreadPriority = threadPriority;
    }

    @Override
    public Thread newThread(final Runnable runnable) {
        Runnable wrapperRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Process.setThreadPriority(mThreadPriority);
                } catch (Throwable t) {

                }
                runnable.run();
            }
        };
        return new Thread(wrapperRunnable);
    }

}

PriorityThreadFactory.java hosted with ❤ by GitHub

创建一个MainThreadExecutor:

public class MainThreadExecutor implements Executor {

    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override
    public void execute(Runnable runnable) {
        handler.post(runnable);
    }
}

MainThreadExecutor.java hosted with ❤ by GitHub

创建一个DefaultExecutorSupplier:

/*
* Singleton class for default executor supplier
*/
public class DefaultExecutorSupplier{
    /*
    * Number of cores to decide the number of threads
    */
    public static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
    
    /*
    * thread pool executor for background tasks
    */
    private final ThreadPoolExecutor mForBackgroundTasks;
    /*
    * thread pool executor for light weight background tasks
    */
    private final ThreadPoolExecutor mForLightWeightBackgroundTasks;
    /*
    * thread pool executor for main thread tasks
    */
    private final Executor mMainThreadExecutor;
    /*
    * an instance of DefaultExecutorSupplier
    */
    private static DefaultExecutorSupplier sInstance;

    /*
    * returns the instance of DefaultExecutorSupplier
    */
    public static DefaultExecutorSupplier getInstance() {
       if (sInstance == null) {
         synchronized(DefaultExecutorSupplier.class){                                                                  
             sInstance = new DefaultExecutorSupplier();      
        }
        return sInstance;
    }

    /*
    * constructor for  DefaultExecutorSupplier
    */ 
    private DefaultExecutorSupplier() {
        
        // setting the thread factory
        ThreadFactory backgroundPriorityThreadFactory = new 
                PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND);
        
        // setting the thread pool executor for mForBackgroundTasks;
        mForBackgroundTasks = new ThreadPoolExecutor(
                NUMBER_OF_CORES * 2,
                NUMBER_OF_CORES * 2,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(),
                backgroundPriorityThreadFactory
        );
        
        // setting the thread pool executor for mForLightWeightBackgroundTasks;
        mForLightWeightBackgroundTasks = new ThreadPoolExecutor(
                NUMBER_OF_CORES * 2,
                NUMBER_OF_CORES * 2,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(),
                backgroundPriorityThreadFactory
        );
        
        // setting the thread pool executor for mMainThreadExecutor;
        mMainThreadExecutor = new MainThreadExecutor();
    }

    /*
    * returns the thread pool executor for background task
    */
    public ThreadPoolExecutor forBackgroundTasks() {
        return mForBackgroundTasks;
    }

    /*
    * returns the thread pool executor for light weight background task
    */
    public ThreadPoolExecutor forLightWeightBackgroundTasks() {
        return mForLightWeightBackgroundTasks;
    }

    /*
    * returns the thread pool executor for main thread task
    */
    public Executor forMainThreadTasks() {
        return mMainThreadExecutor;
    }
}

DefaultExecutorSupplier.java hosted with ❤ by GitHub

注意:不同的线程池可以维护不同数量的线程,就看你的实际需要了

现在可以像下面这样在你的代码中使用它们:

/*
* Using it for Background Tasks
*/
public void doSomeBackgroundWork(){
  DefaultExecutorSupplier.getInstance().forBackgroundTasks()
    .execute(new Runnable() {
    @Override
    public void run() {
       // do some background work here.
    }
  });
}

/*
* Using it for Light-Weight Background Tasks
*/
public void doSomeLightWeightBackgroundWork(){
  DefaultExecutorSupplier.getInstance().forLightWeightBackgroundTasks()
    .execute(new Runnable() {
    @Override
    public void run() {
       // do some light-weight background work here.
    }
  });
}

/*
* Using it for MainThread Tasks
*/
public void doSomeMainThreadWork(){
  DefaultExecutorSupplier.getInstance().forMainThreadTasks()
    .execute(new Runnable() {
    @Override
    public void run() {
       // do some Main Thread work here.
    }
  });
}

UsingThreadPool.java hosted with ❤ by GitHub

通过这种方式,我们能创建不同的线程池为网络任务,I/O任务,很重的后台任务,以及其它的任务。

如何取消一个任务

要取消一个任务,你需要得到那个任务的future。所以你需要用submit方法而不是execute,它可以返回一 个future对象。你就可以通过这个future对象来取消那个任务。

/*
* Get the future of the task by submitting it to the pool
*/
Future future = DefaultExecutorSupplier.getInstance().forBackgroundTasks()
    .submit(new Runnable() {
    @Override
    public void run() {
      // do some background work here.
    }
});

/*
* cancelling the task
*/
future.cancel(true);

CancelTask.java hosted with ❤ by GitHub

如何设置任务的优先级

我们假设在一个队列中有20个任务,但是线程池中只有4个线程。我们依据它们的优先级来执行,因为线程池只能同时执行4个任务。

但是假设我们需要让最后一个进入队列中的任务先执行。我们需要给这个任务设置IMMEDIATE优先级,这样就可以让线程取任务时优先取这个任务去执行(因为它的优先级最高)。

要设置任务的优先级,我们需要创建一个线程池executor。

创建一个优先级枚举:

 /**
 * Priority levels
 */
public enum Priority {
    /**
     * NOTE: DO NOT CHANGE ORDERING OF THOSE CONSTANTS UNDER ANY CIRCUMSTANCES.
     * Doing so will make ordering incorrect.
     */

    /**
     * Lowest priority level. Used for prefetches of data.
     */
    LOW,

    /**
     * Medium priority level. Used for warming of data that might soon get visible.
     */
    MEDIUM,

    /**
     * Highest priority level. Used for data that are currently visible on screen.
     */
    HIGH,

    /**
     * Highest priority level. Used for data that are required instantly(mainly for emergency).
     */
    IMMEDIATE;

}

Priority.java hosted with ❤ by GitHub

创建一个PriorityRunnable:

public class PriorityRunnable implements Runnable {

    private final Priority priority;

    public PriorityRunnable(Priority priority) {
        this.priority = priority;
    }

    @Override
    public void run() {
      // nothing to do here.
    }

    public Priority getPriority() {
        return priority;
    }

}

PriorityRunnable.java hosted with ❤ by GitHub

创建一个PriorityThreadPoolExecutor,它继承自ThreadPoolExecutor。我们还需要创建一个PriorityFutureTask,它实现了Comparable

public class PriorityThreadPoolExecutor extends ThreadPoolExecutor {

   public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
         TimeUnit unit, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit,new PriorityBlockingQueue(), threadFactory);
    }

    @Override
    public Future submit(Runnable task) {
        PriorityFutureTask futureTask = new PriorityFutureTask((PriorityRunnable) task);
        execute(futureTask);
        return futureTask;
    }

    private static final class PriorityFutureTask extends FutureTask
            implements Comparable {
        private final PriorityRunnable priorityRunnable;

        public PriorityFutureTask(PriorityRunnable priorityRunnable) {
            super(priorityRunnable, null);
            this.priorityRunnable = priorityRunnable;
        }
        
        /*
         * compareTo() method is defined in interface java.lang.Comparable and it is used
         * to implement natural sorting on java classes. natural sorting means the the sort 
         * order which naturally applies on object e.g. lexical order for String, numeric 
         * order for Integer or Sorting employee by there ID etc. most of the java core 
         * classes including String and Integer implements CompareTo() method and provide
         * natural sorting.
         */
        @Override
        public int compareTo(PriorityFutureTask other) {
            Priority p1 = priorityRunnable.getPriority();
            Priority p2 = other.priorityRunnable.getPriority();
            return p2.ordinal() - p1.ordinal();
        }
    }
}

PriorityThreadPoolExecutor.java hosted with ❤ by GitHub

首先,在DefaultExecutorSupplier中,我们需要用PriorityThreadPoolExecutor代替ThreadPoolExecutor像下面这个:

public class DefaultExecutorSupplier{

private final PriorityThreadPoolExecutor mForBackgroundTasks;

private DefaultExecutorSupplier() {
  
        mForBackgroundTasks = new PriorityThreadPoolExecutor(
                NUMBER_OF_CORES * 2,
                NUMBER_OF_CORES * 2,
                60L,
                TimeUnit.SECONDS,
                backgroundPriorityThreadFactory
        );

    }
}

DefaultExecutorSupplierP.java hosted with ❤ by GitHub

下面是一个例子展示了如果给一个任务设置HIGH优先级:

/*
* do some task at high priority
*/
public void doSomeTaskAtHighPriority(){
  DefaultExecutorSupplier.getInstance().forBackgroundTasks()
    .submit(new PriorityRunnable(Priority.HIGH) {
    @Override
    public void run() {
      // do some background work here at high priority.
    }
});
}

PriorityTask.java hosted with ❤ by GitHub

通过这种方式,一个任务就可以被设置执行地优先级。

上面的实现也适用于任何JAVA程序。

我用了这个线程池实现在Android Networking Library.

想要了解更多实现细节,你可以把 Android Networking here项目中的DefaultExecutorSupplier.java检出看看。

我希望这些讲解能够帮到你。

谢谢阅读这个文章。如果你喜欢这篇文章,那就在下面点一下❤推荐一下吧。

想了解更多的编程知识,你可以关注我,这样我写了新文章时你会得到通知。

这里看我所有的文章。

谢谢!

Amit Shekhar

你也可以在 Twitter, Linkedin, Github and Facebook找到我。


版权声明

文章版权归本人所有,如需转载需在明显位置处保留作者信息及原文链接 !