Android开发Android知识Android开发经验谈

Android线程池得要这么用

2017-06-22  本文已影响1250人  Only凹凸曼

背景

之前写了两篇关于线程和多线程的文章,竟然写到了多线程,那肯定少不了线程池啊,如果想了解线程和线程池方面的知识可以查看我之前写的
线程你真的了解它吗
这才是真正的多线程
那么什么是线程池,它有什么优点呢?

  1. 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
    能有效控制线程池的最大并发数,避免大量的线程之间因相互抢占系统资源而导致的堵塞线程。
  2. 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
  3. 相对于AsyncTask来说,最大的优势在于:线程可控!比如在离开了某个页面,提交到AsyncTask不能的任务不能撤销,线程池可以在不需要的时候将某个线程移除。

什么是线程池

那么线程池我们该怎么创建呢?首先我们先来看一下创建线程池的构造方法

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

我们先来看一下每一个参数的意义吧。

  1. corePoolSize:核心池的大小,在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。只有当工作队列满了的情况下才会创建超出这个数量的线程。如果某个线程的空闲时间超过了活动时间,那么将标记为可回收,并且只有当线程池的当前大小超过corePoolSize时该线程才会被终止。用户可调用prestartAllCoreThreads()或者prestartCoreThread()方法预先创建线程,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。

  2. maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;当大于了这个值就会将Thread由一个丢弃处理机制来处理。

  3. keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

  4. Unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性。

  5. workQueue:一个阻塞队列,用来存储等待执行的任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。

  6. threadFactory:线程工厂,主要用来创建线程;

  7. handler:表示当拒绝处理任务时的策略,也就是参数maximumPoolSize达到后丢弃处理的方法。有以下四种取值:
    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
    用户也可以实现接口RejectedExecutionHandler定制自己的策略。

线程池Demo

这个是在网上找的一个简单的线程池的实现代码:

使用动态代理方式创建建一个ThreadPoolProxyFactory ,里面提供两种方式获取线程池:普通线程池和下载的线程池。

public class ThreadPoolProxyFactory {
    static ThreadPoolProxy mNormalThreadPoolProxy;
    static ThreadPoolProxy mDownLoadThreadPoolProxy;

    /**
     * 得到普通线程池代理对象mNormalThreadPoolProxy
     */
    public static ThreadPoolProxy getNormalThreadPoolProxy() {
        if (mNormalThreadPoolProxy == null) {
            synchronized (ThreadPoolProxyFactory.class) {
                if (mNormalThreadPoolProxy == null) {
                    mNormalThreadPoolProxy = new ThreadPoolProxy(5, 5);
                }
            }
        }
        return mNormalThreadPoolProxy;
    }

    /**
     * 得到下载线程池代理对象mDownLoadThreadPoolProxy
     */
    public static ThreadPoolProxy getDownLoadThreadPoolProxy() {
        if (mDownLoadThreadPoolProxy == null) {
            synchronized (ThreadPoolProxyFactory.class) {
                if (mDownLoadThreadPoolProxy == null) {
                    mDownLoadThreadPoolProxy = new ThreadPoolProxy(3, 3);
                }
            }
        }
        return mDownLoadThreadPoolProxy;
    }
}

线程池代理,替线程池完成一些操作。提供了三种方法:执行任务,提交任务,移除任务。

public class ThreadPoolProxy {

    ThreadPoolExecutor mExecutor;
    private int mCorePoolSize;
    private int mMaximumPoolSize;


    /**
     * @param corePoolSize    核心池的大小
     * @param maximumPoolSize 最大线程数
     */
    public ThreadPoolProxy(int corePoolSize, int maximumPoolSize) {
        mCorePoolSize = corePoolSize;
        mMaximumPoolSize = maximumPoolSize;
    }

    /**
     * 初始化ThreadPoolExecutor
     * 双重检查加锁,只有在第一次实例化的时候才启用同步机制,提高了性能
     */
    private void initThreadPoolExecutor() {
        if (mExecutor == null || mExecutor.isShutdown() || mExecutor.isTerminated()) {
            synchronized (ThreadPoolProxy.class) {
                if (mExecutor == null || mExecutor.isShutdown() || mExecutor.isTerminated()) {
                    long keepAliveTime = 3000;
                    TimeUnit unit = TimeUnit.MILLISECONDS;
                    BlockingQueue workQueue = new LinkedBlockingDeque<>();
                    ThreadFactory threadFactory = Executors.defaultThreadFactory();
                    RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();

                    mExecutor = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, keepAliveTime, unit, workQueue,
                            threadFactory, handler);
                }
            }
        }
    }
    /**
     执行任务和提交任务的区别?
     1.有无返回值
        execute->没有返回值
        submit-->有返回值
     2.Future的具体作用?
        1.有方法可以接收一个任务执行完成之后的结果,其实就是get方法,get方法是一个阻塞方法
        2.get方法的签名抛出了异常===>可以处理任务执行过程中可能遇到的异常
     */
    /**
     * 执行任务
     */
    public void execute(Runnable task) {
        initThreadPoolExecutor();
        mExecutor.execute(task);
    }

    /**
     * 提交任务
     */
    public Future submit(Runnable task) {
        initThreadPoolExecutor();
        return mExecutor.submit(task);
    }

    /**
     * 移除任务
     */
    public void remove(Runnable task) {
        initThreadPoolExecutor();
        mExecutor.remove(task);
    }
}

那么我们要怎么使用线程池呢:

ThreadPoolProxyFactory .getNormalThreadPoolProxy().execute(Runnable);

总结

这个大概就是Android线程池的使用,有什么不对的地方欢迎大家留言,我们一起交流,写下大家。
在网上也看到了一个关于实现优先级线程池的有兴趣的朋友也可以学习一下实现优先级线程池

上一篇下一篇

猜你喜欢

热点阅读