ThreadPoolExecutor创建线程池

1,880 阅读4分钟

简介

之前在看《阿里巴巴安卓开发手册》的时候,在进程、线程与消息通信里面讲到关于创建线程的规范,然后总结一下记录下来。

线程池的优势

我们在平时的开发中,可能会经常需要创建线程来执行任务,android中本身提供了AsyncTask来创建线程,我们也可以通过java自带的ThreadPoolExecutor来创建线程池,不允许在应用中直接通过new Thread()方法来创建线程。

好处:
使用线程池的可以减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。另外创建匿名线程不便于后续的资源使用分析,对性能造成困扰。

而为了规范线程池的创建,不允许使用Executors创建线程池,Executors提供了了很多创建线程池的方法,其中FixedThreadPool和SingleThreadPool允许的请求长度可以设置为Integer.MAX_VALUE,可能导致堆积大量请求,从而导致OOM。总之,使用Executors不利于统一创建线程池的规范。

ThreadPoolExecutor简介

ThreadPoolExecutor类位于java.util.concurrent包下面,主要提供了四个构造方法

public class ThreadPoolExecutor extends AbstractExecutorService{
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);
    
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory);
    
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler);
    
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
}

构造器中各个参数的含义如下:

  • corePoolSize: 核心池的大小。默认情况下,在创建线程池后,线程池的线程数为0,当有新任务时,就会创建一个线程去执行任务,当线程池中的线程数量达到corePoolSize后,就会把到达的任务放到缓存队列当中
  • maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程
  • keepAliveTime: 表示线程没有任务执行时最多存活时间。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用
  • unit: 参数keepAliveTime的时间单位
  • workQueue: 一个阻塞队列,用来存储等待执行的任务。
  • threadFactory: 线程工厂,用来创建线程
  • handler: 表示当拒绝处理任务时的策略,有四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

这里只是简单介绍一下这个类,想深入了解的读者可以看一下官方文档或者网上看相关博客。

使用ThreadPoolExecutor创建线程池

下面我们来看在代码中如何创建ThreadPoolExecutor来创建线程池:

public class PriorityThreadFactory implements ThreadFactory {
    private final int mPriority;

    public PriorityThreadFactory(int mPriority) {
        this.mPriority = mPriority;
    }

    @Override
    public Thread newThread(Runnable runnable) {
        Runnable wrapperRunnable = () -> {
            try{
                Process.setThreadPriority(mPriority);
            }catch (Throwable t){

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

这个类主要是用来创建线程池,同时我们可以指定线程的优先级。
创建DefaultExecutorSupplier类,这个类用单列来维护线程池

public class DefaultExecutorSupplier {
  
    private static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
    
    private static final int KEEP_ALIVE_TIME = 10;

    private final ThreadPoolExecutor mForBackgroundTasks;

    private static DefaultExecutorSupplier sInstance;

    public static DefaultExecutorSupplier getInstance(){
        if(sInstance == null){
            synchronized (DefaultExecutorSupplier.class){
                sInstance = new DefaultExecutorSupplier();
            }
        }
        return sInstance;
    }

    private DefaultExecutorSupplier(){
        ThreadFactory backgroundPriorityThreadFactory = new PriorityThreadFactory(Process.THREAD_PRIORITY_DEFAULT);

        mForBackgroundTasks = new ThreadPoolExecutor(
                NUMBER_OF_CORES * 2,
                NUMBER_OF_CORES * 2,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                backgroundPriorityThreadFactory
        );

    }

    public ThreadPoolExecutor getForBackgroundTasks(){
        return mForBackgroundTasks;
    }
}

这个类用单例模式来创建线程池。

然后在任何你需要你需要调用的地方通过下面的方法来调用

 DefaultExecutorSupplier
                .getInstance()
                .getForBackgroundTasks()
                .execute(() -> {
                    //你需要在子线程执行的任务
            });

当然,如果你想要创建可以取消的任务,可以通过下面的方法:

//通过future执行线程池
Future future = DefaultExecutorSupplier
                    .getInstance()
                    .getForBackgroundTasks()
                    .submit(() -> {
                        //你需要在子线程执行的任务
                    });

//在需要取消线程任务的地方调用cancel()方法
future.cancel(true)

总结

通过ThreadPoolExecutor类可以规范项目中线程的创建,通过单例模式维护一个统一的线程池,可以很好的规避频繁通过new Thread()创建线程的开销,方便项目中线程的管理

参考文献