• Android中的线程池 ThreadPoolExecutor


    线程池的优点:

    1. 重用线程池中的线程,避免因为线程的创建和销毁带来的性能消耗
    2. 能有效的控制线程的最大并发数,避免大量的线程之间因抢占系统资源而导致的阻塞现象
    3. 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能

    ThreadPoolExecutor:

    Android中,用ThreadPoolExecutor来实现线程池的配置。
    ThreadPoolExecutor文档中文版
    ThreadPoolExecutor文档英文版

    QQ截图20160711141312.png

    ThreadPoolExecutor的构造方法

    ThreadPoolExecutor的构造方法有四个,其实现如下:
    ```
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
    TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    Executors.defaultThreadFactory(), defaultHandler);
    }

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                              TimeUnit unit, BlockingQueue<Runnable> workQueue, 
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                threadFactory, defaultHandler);
    }
    
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
                              TimeUnit unit, BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                Executors.defaultThreadFactory(), handler);
    }
    
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
                              TimeUnit unit,BlockingQueue<Runnable> workQueue, 
                              ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
    ```
    
    构造方法的参数
    • corePoolSize
      程池中的核心线程数,也就是是线程池中的最小线程数;
      核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出;

    • maximumPoolSize
      最大线程池大小,当活动线程数达到这个值,后续任务会被阻塞

    • keepAliveTime
      线程池中超过corePoolSize数目的非核心线程最大存活时间;闲置时的超时时长,超过这个值后,闲置线程就会被回收

    • unit
      keepAliveTime 参数的时间单位。这是一个枚举,详情请参考TimeUnit

    • workQueue
      执行前用于保持任务的队列,也就是线程池的缓存队列。此队列仅保持由 execute 方法提交的 Runnable 任务
      关于三种提交策略这篇文章不错

    • threadFactory
      线程工厂,为线程池提供创建新线程的功能,它是一个接口,只有一个方法:Thread newThread(Runnable r)

    • RejectedExecutionHandler
      线程池对拒绝任务的处理策略。一般是队列已满或者无法成功执行任务,这时ThreadPoolExecutor会调用handler的rejectedExecution方法来通知调用者
      ThreadPoolExecutor默认有四个拒绝策略:

        1、ThreadPoolExecutor.AbortPolicy()   直接抛出异常RejectedExecutionException
        2、ThreadPoolExecutor.CallerRunsPolicy()    直接调用run方法并且阻塞执行
        3、ThreadPoolExecutor.DiscardPolicy()   直接丢弃后来的任务
        4、ThreadPoolExecutor.DiscardOldestPolicy()  丢弃在队列中队首的任务
      

    也可以自己继承RejectedExecutionHandler来写拒绝策略.

    ThreadPoolExecutor的执行过程:

    一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。

    1. 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程
    2. 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
    3. 当提交任务数超过【maximumPoolSize+阻塞队列大小】时,新提交任务由RejectedExecutionHandler处理 (关于这里,网上90%以上的人说当任务数>=maximumPoolSize时就会被拒绝,我不知道依据在哪里,也不知道代码验证过没,经过我的验证这种说法是不成立的,具体的看下边日志分析)
    4. 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
    5. 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
    定制自己的线程池:
    public class ThreadTestActivity extends AppCompatActivity {
        private final int CORE_POOL_SIZE = 1;//核心线程数
        private final int MAX_POOL_SIZE = 3;//最大线程数
        private final int BLOCK_SIZE = 2;//阻塞队列大小
        private final long KEEP_ALIVE_TIME = 2;//空闲线程超时时间
        private ThreadPoolExecutor executorPool;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_thread_test);
            //创建线程池
            // 创建一个核心线程数为3、最大线程数为8,缓存队列大小为5的线程池
            executorPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME,
                    TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(BLOCK_SIZE),
                    Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
            executorPool.allowCoreThreadTimeOut(true);
        }
    
        public void begin(View view) {
            for (int num = 0; num < 6; num++) {//每个500ms添加一个任务到队列中
                try {
                    Li("execute");// 监听相关数据
                    executorPool.execute(new WorkerThread("thread-" + num));
                } catch (Exception e) {
                    Log.e("threadtest", "AbortPolicy...");
                }
            }
    
            // 20s后,所有任务已经执行完毕,我们在监听一下相关数据
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(20 * 1000);
                    } catch (Exception e) {
    
                    }
                    Li("monitor after");
                }
            }).start();
        }
    
        private void Li(String mess) {
            Log.i("threadtest", "monitor " + mess
                            + " CorePoolSize:" + executorPool.getCorePoolSize()
                            + " PoolSize:" + executorPool.getPoolSize()
                            + " MaximumPoolSize:" + executorPool.getMaximumPoolSize()
                            + " ActiveCount:" + executorPool.getActiveCount()
                            + " TaskCount:" + executorPool.getTaskCount()
    
            );
        }
    
    }
    
    // 模拟耗时任务
    public class WorkerThread implements Runnable {
        private String threadName;
        public WorkerThread(String threadName) {
            this.threadName = threadName;
        }
        @Override
        public synchronized void run() {
    
            int i = 0;
            boolean flag = true;
            try {
                while (flag) {
                    Thread.sleep(1000);
                    i++;
                    Log.e("threadtest", "WorkerThread " + threadName + "  " + i);
                    if (i >2) flag = false;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public String getThreadName() {
            return threadName;
        }
    }
    

    日志信息:


    QQ截图20160712161004.png

    下面就来对日志信息进行分析:

    1. 上边粉色部分(1~6行),可以看到poolsize逐渐累加,一直加到最大线程数后不再增加,这呼应了上述“执行过程1”
    2. 接下来绿色部分()7行,验证了上边我们说的“执行过程3”,缓存队列数为2,最大线程数为3,共有6条任务,所以会有【 6-(2+3)】条任务被拒绝,这里拒绝策略我们用的是ThreadPoolExecutor.AbortPolicy()也就是直接抛出异常,也就是我们日志的第7行
    3. 然后8~22行是任务的执行过程,
    4. 其中蓝色部分(8~16)行,我们可以看到有3条任务在同时执行,也就是最大线程数
    5. 接下来的绿色(17~22行),在三条任务执行完成后,剩余的排队任务才开始执行
    6. 最后,23行,20s后,线程都处于空闲状态,所以非核心线程会被回收,但是因为代码中我们设置了executorPool.allowCoreThreadTimeOut(true),所以这时处于空闲状态的核心线程也会被回收,这时池中的线程数为0
    关于线程池的一些建议
    • 最大线程数一般设为2N+1最好,N是CPU核数

    官方定义的四种线程池

    其实,本应该先说官方定义的这四种线程池,然后再说自定义线程池,但是考虑到里边的一些配置参数,所以本帖先利用自定义线程池把各个配置参数理一下,然后再讲官方定义的四种线程池,这样也便于理解官方定义的这四种线程池
    这四种线程池都是通过Executors的工厂方法来实现

    1、FixedThreadPool

    他是一种数量固定的线程池,且任务队列也没有大小限制;
    它只有核心线程,且这里的核心线程也没有超时限制,所以即使线程处于空闲状态也不会被回收,除非线程池关闭;
    当所有的任务处于活动状态,新任务都处于等待状态,知道所有线程空闲出来;
    因为它不会被回收,所以它能更快的响应;
    源码:

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

    实现:

        ExecutorService service = Executors.newFixedThreadPool(3);
        service.execute(new WorkerThread("thread-" + num));
    
    2、CachedThreadPool

    无界线程池,可以进行自动线程回收
    他是一种线程数量不固定的线程池;
    它只有非核心线程,且最大线程数为Integer.MAX_VALUE,也就是说线程数可以任意大;
    当池中的线程都处于活动状态时,会创建新的线程来处理任务,否则会利用空闲线程来处理任务;所以,任何添加进来的任务都会被立即执行;
    池中的空闲线程都有超时限制,为60s,超过这个限制就会被回收,当池中的所有线程都处于闲置状态时,都会因超时而被回收,这个时候,她几乎不占用任何系统资源;
    适合做大量的耗时较少的任务;
    源码:

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

    实现:

        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new WorkerThread("thread-"));
    
    3、SingleThreadExecutor

    只有一个核心线程,所有任务都在同一线程中按序执行,这样也就不需要处理线程同步的问题;
    源码:

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

    实现:

        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new WorkerThread("thread-"));
    
    4、ScheduledThreadPool

    它的核心线程数量是固定的,而非核心线程是没有限制的,且非核心线程空闲时会被回收;
    适合执行定时任务和具有固定周期的任务
    源码:

        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
        }
        public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
            return new DelegatedScheduledExecutorService
                (new ScheduledThreadPoolExecutor(1));
        }
        public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE,
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                  new DelayedWorkQueue());
        }
    

    实现:

        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        或
        ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();
    
        threadPool.schedule(runnable, 20, TimeUnit.SECONDS);// 20秒后执行任务
        或
        threadPool.scheduleAtFixedRate(runnable,10,20,TimeUnit.SECONDS);//延迟10s,每20s执行一次任务
    

    由于本人技术有限,避免不了出现一些错误或者理解有偏差描述不清楚的地方,请大家谅解并提醒我:)

    上一篇:AsyncTask
    再来一篇:java中的thread

    更多内容请关注我的Android专题
    本文出自:http://www.jianshu.com/users/c1b4a5542220/latest_articles
    转载请注明出处!



    作者:乆_丩
    链接:http://www.jianshu.com/p/3da543063b8c
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    ios-表视图-demo4-内容自己适应高度
    ios-表视图-demo3-单选
    应用管理的实现
    初识MVC和KVC
    Xcode的常用快捷键
    UI基础--手写代码实现汤姆猫动画
    UI基础--UIView常见属性之frame、center、bounds、transframe属性
    UI基础--UIButton、懒加载
    ios多线程
    ios多线程简介
  • 原文地址:https://www.cnblogs.com/ldq2016/p/8056744.html
Copyright © 2020-2023  润新知