深入理解Android线程池

来源:互联网 发布:修改js中的字体颜色 编辑:程序博客网 时间:2024/05/02 00:53

请尊重作者劳动成果,转载请标明原文链接:http://blog.csdn.net/capzhaot/article/details/51727555

1. 使用线程池的优势

在安卓开发中,开启子线程使我们经常接触到的,安卓系统为了App能够流畅的运行,把主线程作为UI-Thread,也就是关于UI绘制相关的动作都必须在主线程中执行,因此,一些耗时操作,比如访问网络,文件操作等等就必须放到子线程中去执行,这就保证了App的页面的流畅性不受耗时操作的影响.

1.1 传统方式开启子线程的缺点:

在开启一个异步任务时,我们最常见的一个写法就是直接new Thread,比如:

new Thread(new Runnable() {      @Override      public void run() {         // task    }  }).start();  

这么写的确很方便,但是缺点也很明显:

  1. 由于采用匿名的方式,导致该子线程无法被管理.比如停止,查看异步任务的进度等等.
  2. 如果需要大量执行异步任务,子线程就会被频繁的创建,销毁,导致系统资源消耗增加.

1.2 使用线程池的好处:

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

2. Android中线程池的实现:

Android中线程池的概念来源于Java,核心实现为ThreadPoolExecutor类,该类提供了一系列参数来配置线程池,根据其配置的参数不同,Android中线程池主要分为四类,以实现不同特点的线程池.下面我们先来认识一下ThreadPoolExecutor类,了解其参数如何配置,然后再详细介绍四类特点各异的线程池.

2.1 ThreadPoolExecutor类介绍:

首先看一下其构造函数:

    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler)

corePoolSize
这个参数表示线程池的核心线程数,核心线程即使空闲也会一直保持存活状态,除非你设置了allowCoreThreadTimeOut(核心线程超时属性)为true.此时闲下来的核心线程一旦超过了keepAliveTime指定的时间,核心线程就会被终止.
核心线程与普通线程

maximumPoolSize
表示线程池能容纳的最大线程数量,当池中线程达到此数量时,后续的任务将会被阻塞.

keepAliveTime
当池中线程数量超过核心线程数量时,这些多出的线程如果空闲的话,它的存活时间就为keepAliveTime.超过这个时间,该线程就会被终止.
当allowCoreThreadTimeOut属性设定后,keepAliveTime也会作用于核心线程.

unit
指定keepAliveTime的时间单位,常见的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等.

workQueue
指定线程池的任务队列,提交的线程会在此队列中排队.

threadFactory
线程工厂,为线程池的任务创建新的线程,他只有一个方法: Thread newThread(Runnable r).

handler
当线程池已满或者无法继续执行新的任务时,这个时候ThreadPoolExecutor会调用handler的rejectedEcecutor方法来通知调用者.这个参数不太常用.

ThreadPoolExecutor线程创建规则:

(1) 如果线程池的中线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务.
(2)如果线程池中的线程数量达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行.
(3)如果任务无法被插入到任务队列中,这往往是因为队列已满,若此时线程数量未超过线程池规定的最大值,那么会立即启动一个非核心线程来执行任务.
(4)如果池中数量以达到规定的最大值,那么就拒绝执行任务,这个时候ThreadPoolExecutor会调用rejectedEcecution方法来通知调用者.


3. 线程池的分类:

通过对ThreadPoolExecutor参数的不同配置,可以实现Android中四种常见的具有不同功能的线程池,这四类线程池分别是FixedThreadPool,CacheThreadPool,SckeduleThreadPool,以及SingleThreadExecutor.他们可分别通过Executors类中的对应方法来创建.

1. FixedThreadPool(线程数量固定的线程池)

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

Features:
- 最大线程数与核心线程数相同,因此该线程池只有核心线程.
- keepAliveTime为0,说明核心线程没有超时机制,会一直保持active状态.
- 任务队列使用LinkedBlockingQueue,表示任务队列的大小没有限制.

通过对ThreadPoolExecutor的配置我们发现,FixedThreadPool线程池只有核心线程(就像一架飞机,只有商务舱),即使当线程处于空闲状态也不会被回收,除非线程池关闭. 当核心线程数量满时,新的任务就会处于等待状态,直到有核心线程空闲下来.
优势:
由于FixedThreadPool只有核心线程并且不会被回收,这意味着它能更快速度响应外界请求.

2. CacheThreadPool(线程数量不定的线程池 )

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

Features:
- 没有核心线程,只有非核心线程,并且最大线程数为任意大.(一架全部都是经济舱的飞机…)
- 有超时机制,空闲的线程超过60s就会被回收.
- 当线程池的线程都处于active状态时,线程池会创建新的线程来处理新的任务.
优势:
CacheThreadPool的SynchronousQueue队列是一个非常特殊的队列,基本可以理解为一个无法插入元素的队列.因此,这将导致任何任务都会被立即执行,从这点看,这类线程比较适合执行大量的耗时较少的任务.而当整个线程池都处于闲置状态时,所有的线程都因超时被停止,因此占用系统资源极少.

3. SckeduleThreadPool( 可调度线程池)

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {        return new ScheduledThreadPoolExecutor(corePoolSize);    }    public ScheduledThreadPoolExecutor(int corePoolSize) {        super(corePoolSize, Integer.MAX_VALUE,              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,              new DelayedWorkQueue());    }        //super方法        public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             Executors.defaultThreadFactory(), defaultHandler);    }

Features:
- 核心线程数量固定,非核心线程数量无限制.

由于使用的是DelayedWorkQueue,这类线程池主要用于执行定时任务和具有固定周期的重复任务.

4. SingleThreadExecutor( 单核心线程池)

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

Features:
- 内部只有一个核心线程,没有非核心线程,确保所有任务都在同一个线程中按顺序执行.
- SingleThreadExecutor的意义是统一外界所有的线程到一个线程中,保证其顺序性,这使得其不需要处理线程同步的问题.


4. 四种线程池的简单举例:

        Runnable r = new Runnable() {            @Override            public void run() {                SystemClock.sleep(1000);            }        };        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);        fixedThreadPool.execute(r);        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();        cachedThreadPool.execute(r);        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);        //延时2s后执行        scheduledThreadPool.schedule(r, 2000, TimeUnit.MILLISECONDS);        //延时1s后,每隔1s执行一次.        scheduledThreadPool.scheduleAtFixedRate(r, 1000, 1000, TimeUnit.MILLISECONDS);        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();        singleThreadExecutor.execute(r);
0 0
原创粉丝点击