Android进阶-线程池

作者: zone7_ | 来源:发表于2017-05-31 00:27 被阅读116次

    在上一篇文章(为什么要学习) AsyncTask 原理 提到了线程池,那么现在来学习学习 Android 的线程池。那么来看看线程池有什么优点,或者说为什么要学习线程池。

    • 重用线程池中的线程,避免了新建和销毁线程的内存开销。
    • 能有效线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象。
    • 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。

    基础线程池

    常用的线程池有挺多的,首先来看看基础线程池 ThreadPoolExcutor ,其他常见线程池都是基于 ThreadPoolExcutor 去实现的。那么我们先从它的构造方法入手。

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

    参数:corePoolSize
    线程池的核心线程数,默认情况下核心线程会一直存在,即便是线程处于闲置状态。
    参数:maximumPoolSize
    线程池所能容纳的最大线程数,当核心线程不够用时,就会开启非核心线程来处理任务,maximumPoolSize = corePoolSize + 非核心线程数
    参数:keepAliveTime
    当非核心线程处理完任务处于闲置闲置状态时,如果在一定时间范围内没有被重用,就会被销毁。而 keepAliveTime 就是前面所指的一定时间范围。
    参数:unit
    有了时间范围,当然要确定时间的单位。而这个参数就是描述时间范围的参数。这是一个枚举参数,常用的有:
    TimeUnit.DAYS
    TimeUnit.HOURS
    TimeUnit.MINUTES
    参数:workQueue
    线程池的任务队列,线程池的 excute 方法提交的任务会进入这个队列。
    参数:threadFactory
    为线程池提供新建线程的方法--> newThread(Runnale r)

    ThreadPoolExcutor 执行任务时大致遵循的规则:

    1. 如果线程池中的线程数量还没达到核心线程数,则新开启一个核心线程来执行任务
    2. 如果已经达到或者超过核心线程的数量,则会被插入到任务队列中,等待执行
    3. 如果任务队列已满,则会新开启一个非核心线程来执行任务。
    4. 如果线程数达到最大值,则会拒绝执行任务。

    ok,解释了那么多还不如来 实践一下,下面看看小例子:

    mExecutor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));//初始化 mExecutor 
    
    fab.setOnClickListener(new View.OnClickListener() {//设置一个点击事件
        @Override
        public void onClick(View view) {
            num++;
            mExecutor.execute(new MyRunnable(String.valueOf(num)));
            Log.d(TAG, "队列中等待执行的任务数: "+String.valueOf(mExecutor.getPoolSize()));
            Log.d(TAG, "执行完毕是数量: "+String.valueOf(mExecutor.getCompletedTaskCount()));
        }
    });
            
    
    

    常用线程池

    FixedThreadPool
    这是一种只有核心线程的线程池,线程都不会被回收,而且意味着能更快地响应任务,并且任务队列也没有大小限制。
    FixedThreadPool 实例化方法:

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

    实例:

    ExecutorService e = Executors.newFixedThreadPool(5);
    e.execute(new MyRunnable(String.valueOf(num)));
    

    CachedThreadPool
    这是一种线程数不确定的线程池,由下面的实例化方法可以看出,核心线程为0,非核心线程为 Integer.MAX_VALUE ,这是一个很大的数。超时策略为 60 秒。另外,SynchronousQueue 是一个无法存储元素的队列,也就是说,每当有新任务,就会立即开启一个线程。而如果全部任务都处理完成,线程池中也可能存在 0 个线程的情况。所以,几乎不占用任何系统资源。
    CachedThreadPool 实例化方法:

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

    实例:

    ExecutorService e2 = Executors.newCachedThreadPool();
    e2.execute(new MyRunnable(String.valueOf(num)));
    

    ScheduledThreadPool
    从下面的实例化方法可以看出,这是一个核心线程固定,非核心线程很大很大的一个线程池。DEFAULT_KEEPALIVE_MILLIS = 10L,也就是说超时策略为 10 毫秒。
    实例化方法:

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

    实例:

    ExecutorService e3 = Executors.newScheduledThreadPool(5);
    e3.execute(new MyRunnable(String.valueOf(num)));
    

    SingleThreadPool
    从下面的实例化方法中,可以看出,这个线程池,只有一个核心线程。它存在的意义就是确保所有任务都在同一个线程执行,使得这些任务不需要处理线程同步的问题。
    实例化方法:

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

    实例:

    ExecutorService e4 = Executors.newSingleThreadExecutor();
    e4.execute(new MyRunnable(String.valueOf(num)));
    

    总结

    进过一系列的学习,对于线程池也是有了初步的了解,但是了解归了解,运用到实际开发中优势另外一回事。还得多与实际相结合。

    参考文献:《Android开发艺术探索》

    相关文章

      网友评论

        本文标题:Android进阶-线程池

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