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
來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。






本文转自 一点点征服 博客园博客,原文链接http://www.cnblogs.com/ldq2016/p/8056744.html:,如需转载请自行联系原作者

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
571 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
286 4
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
555 5
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
650 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
456 11
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享安卓与iOS开发中的线程管理比较
【8月更文挑战第30天】本文将探讨网络安全与信息安全的重要性,并分享关于网络安全漏洞、加密技术和安全意识的知识。我们将了解常见的网络攻击类型和防御策略,以及如何通过加密技术和提高安全意识来保护个人和组织的信息安全。