Android中的线程池

作者: 大虾啊啊啊 | 来源:发表于2018-08-27 15:37 被阅读36次

    前言

    提到线程池,我们先说一下使用线程池的好处。使用线程池的优点可以概括为:
    1、重复使用线程池中的线程,避免因为线程的创建和销毁带来的性能的开销而影响系统性能。
    2、可以有效的控制线程的最大并发数,避免大量的线程之间因为互相抢占资源而导致堵塞现象。
    3、对线程进行简单的管理,并提供定时执行以及循环间隔执行等功能。

    一、ThreadPoolExecutor

    在Android中的线程池来源于Java中的Executor,Executor是一个接口,真正的线程池实现类为Executor。下面我们来介绍一下ThreadPoolExecutor的创建方法。

        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 threadFactory, defaultHandler);
        }
    

    构造方法中有5个参数,接下来我们分别介绍这5个参数的含义:

    corePoolSize:
    线程池中核心的线程数,在默认的情况下核心线程会一直存活,即使是线程池中的线程处于闲置的状态。但是当ThreadPoolExecutor的allowCoreThreadTimeOut的属性设置为true的时候,即允许线程池中的核心线程被终止,通过keepAliveTime这个参数决定,这个参数意思就是线程池的闲置时长。当核心线程处于闲置状态并且超过了keepAliveTime超时时长,那么核心线程也会被终止(前提是allowCoreThreadTimeOut = true)
    maximumPoolSize:
    线程池中的最大线程数,当活动线程超过了最大线程数,那么后续的新任务就会处于阻塞状态。
    keepAliveTime:
    默认情况下指的是线程池中的非核心线程闲置的超时时长,当超过了这个时间,非核心线程就会被终止。当核心线程的allowCoreThreadTimeOut 属性为true的时候,核心线程闲置超过了该时长,也会被终止。
    unit:
    超时时长的单位
    workQueue:
    线程池中的任务队列,ThreadPoolExecutor中的execute方法传入的Runnable对象会存到盖队列中。
    threadFactory:
    线程工厂,为线程池创建新线程。
    ThreadPoolExecutor执行任务的时候大致遵循以下原则,当有新任务要执行的时候。
    (1)如果线程池中的核心线程未达到设置的核心线程数,那么就会开启一个核心线程执行新任务。
    (2)如果线程池中的核心线程已经达到了设置的核心线程数,那么新的任务就会插入到线程池中的任务队列中等待被执行。
    (3)如果(2)中的任务队列已满,并且线程池中的线程数量未达到设置的最大线程数,那么就会创建一个非核心线程去执行任务。
    (4)如果(3)中线程池中的线程数已经达到了最大线程数,那么ThreadPoolExecutor就会调用RejectedExecutionHandler的rejectedExecution方法,通知调用者。

    二、Android中常用的线程池分类

    在Android中常用的线程池有四种:FixThreadPool、CachedThreadPool、ScheduleThreadPool、SingleThreadExecutor
    (1)FixThreadPool(一堆人上公厕)
    我们先看下FixThreadPool的初始化方法:

     public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }
    

    我们看到核心线程数和最大线程数是一样的。所以使用FixThreadPool的时候,核心线程是固定的,并且没有非核心线程,任务都在核心线程中执行,当任务数超过了核心线程数,那么新任务就会在任务队列中排队。
    (2)CachedThreadPool(一堆人去一家很大很大的咖啡馆)

    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
    
    

    从源码中我们看到,没有核心线程,并且非核心线程为Integer.MAX_VALUE。这个值非常大,所以每次有新任务的时候,当线程池中没有闲置的线程,就会创建新的线程执行新任务。线程池中的任务队列为SynchronousQueue,该队列可以当做一个无法存储元素的队列。
    (3)ScheduleThreadPool

    public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE,
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                  new DelayedWorkQueue());
        }
    
    
        public ScheduledThreadPoolExecutor(int corePoolSize,
                                           ThreadFactory threadFactory) {
            super(corePoolSize, Integer.MAX_VALUE,
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                  new DelayedWorkQueue(), threadFactory);
        }
    

    ScheduleThreadPool的源码中可以看到,核心线程数是固定的,非核心线程不限制大小,当非核心线程闲置的时候,会被立即回收。任务队列我们看到是DelayedWorkQueue,从而看出,ScheduleThreadPool主要作用就是执行具有循环周期的定时任务。
    (4)SingleThreadExecutor(公厕上只有一个坑位)
    从英文单词上可以看出,就是单例的意思,我们来看下具体的实现:

    public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }
    
    

    从源码中可以看到,核心线程数和非核心线程数都是固定的,当线程池中的线程非闲置的情况下,新的任务要插入到任务队列中排队。SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,使得任务之间不需要解决线程同步的问题。

    小结

    以上我们就简单的介绍了Android中的线程池的意义,初始化方法,以及在Android常用的线程池。

    使用例子

     ivEtxt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.e(TAG, "run: "+Thread.currentThread().getName() );
                    ExecutorService fixedThreadPool =  Executors.newFixedThreadPool(10);
                    fixedThreadPool.execute(new Runnable() {
                        @Override
                        public void run() {
                            Log.e(TAG, "run: "+Thread.currentThread().getName() );
                        }
                    });
                }
            });
    
    08-27 15:31:34.567 3055-3055/com.mujin.keji.myapplication E/MainActivity: run: main
    08-27 15:31:34.568 3055-3079/com.mujin.keji.myapplication E/MainActivity: run: pool-2-thread-1
    

    初始化FixedThreadPool后,调用execute方法,传入Runnable对象,在run()方法中执行的任务就是在线程池中的线程执行的。我们看打印结果就知道,线程池的名字是不一样的。

    相关文章

      网友评论

        本文标题:Android中的线程池

        本文链接:https://www.haomeiwen.com/subject/iulziftx.html