Android中的线程池简介

来源:互联网 发布:山东服务器数据恢复 编辑:程序博客网 时间:2024/06/03 21:32

Android中的线程池的概念来源于Java中的Executor,Executor是一个接口,其真正的实现类是ThreadPoolExecutor。ThreadPoolExecutor提供了一系列的参数来配置线程池,通过不同的参数来创建不同的线程池。在了解具体的线程池之前,我们先大概了解一下ThreadPoolExecutor的概念。

  • ThreadPoolExecutor
    ThreadPoolExecutor是线程池的真正实现,他的构造方法提供了一系列的参数来配置线程池。

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

    corePoolSize
    线程池的核心线程数量,默认情况下,核心线程在线程池中会一直存活下去,及时他们处于闲置状态,如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么在核心线程闲置的的情况下会有超时策略,时间由keepAliveTime指定,超出预定时间之后,核心线程会被终止。

    maximumPoolSize
    线程池的最大线程数,当线程池中的线程达到该数量之后,后续的任务会被放在队列中等待。

    keepAliveTime
    非核心线程的超时时间,超过该时长的闲置非核心线程,会被回收。参见第一条,该事件同样可以作用于核心线程。

    unit
    用于指定keepAliveTime参数的时间单位,该类是一个枚举。

    workQueue
    线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在该队列中。

    threadFactory
    线程工厂,为线程池提供创建新线程的能力。是一个接口,需要去实现它。

    上边的几个参数是ThreadPoolExecutor的几个主要参数,还有一些不太常用的参数,不常用的参数中有一个比较重要,是RejuetedExecutionHandler,如果当前线程池无法执行新任务的时候,可能是由于任务队列已满或其他原因的时候,会调用该参数的rejectExecution方法,默认情况下会抛出一个RejectedExecutionException。

    ThreadPoolExecutor执行任务的时候大致会遵循以下几个规则:

    1. 如果线程池中的线程数量没有达到核心线程数量的上限的时候,会直接启动一个新线程去执行新任务。
    2. 如果线程池中的线程数量已经达到或者超出核心线程的数量的时候,新任务会被插入到队列中等待。
    3. 如果在步骤2中无法将任务插入到队列中,比如队列已满,这时候如果线程池中的线程数量没有达到线程池的上限,这时候会立即启动一个非核心线程来执行任务。
    4. 如果步骤3中的线程数量已经达到线程池规定的最大数量,那么线程池就会拒绝执行任务,默认会抛出RejectedExecutionException。

    读者如果对AsyncTask有一定了解或者查看过他的源码的话,会发现AsyncTask中使用的就是两个线程池加一个handler的组合,其中一个线程池用于排队,另一个线程池用于真正的执行任务,handler则用于线程的切换。

    其中用于真正执行任务的那个线程,通过查看源码可以看到他的配置如下:

    • 核心线程的数量等于CPU核心数+1
    • 线程池的最大数量等于CPU核心数*2+1
    • 核心线程没有超时机制,非核心线程闲置超时为1秒
    • 任务队列上限数量为128
  • 线程池的分类
    Android中的线程池主要分为四类:FixedThreadPool、CachedhreadPool、ScheduledThreadPool和SingleThreadPool。

    1.FixedThreadPool
    通过Executors的newFixedThreadPool方法来创建。他是一种线程数量固定的线程池,当线程处于空闲状态的时候,他们并不会被回收,除非线程池被关闭了。并且其核心线程没有超时机制,任务队列也没有大小限制。

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

    2.CachedThreadPool
    通过Executors的newCachedThreadPool方法来创建。他是一种线程数量保定的线程池,他只有非核心线程,其线程池最大数量为Integer.MAX_VALUE,基本可以认为是任意大小,没有上限。当有新任务来到的时候,如果线程池中的线程都处于活动状态的时候,线程池会立即创建新的线程来处理该任务,否则就利用空闲的线程来处理新的任务。该线程池的超时时间为60s,超时之后线程会被回收。因为线程池的线程数量几乎可以无限大,所以在该线程池中,workQueue中是没有任务在排队的,每当有新任务来的时候都会被立刻执行。那么在这样的情况下,该线程池适合执行一些比较频繁,耗时较少的任务。

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

    3.ScheduledThreadPool
    通过Executors的newScheduledThreadPool方法来创建,他的核心线程数量是固定的,而非核心线程的数量则没有限制,并且当非核心线程闲置的时候会立刻被回收,该类线程池主要用于执行数量相对比较固定的定时任务或者有固定周期的重复任务等。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){    return new ScheduledThreadPoolExecutor(corePoolSize);}public ScheduledThreadPoolExecutor(int corePoolSize){    super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue());}

    4.SingleThreadExecutor
    通过Executors的newSingleThreadExecutor方法来创建,该线程池内部只要一个核心线程,它确保所有的任务都在该线程中顺序执行,他的意义在于统一所有的任务到一个线程中,使这些任务之间不需要处理线程之间的同步问题。

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

    以上就是Android中最典型的四种线程池的基本介绍,除了这几种线程池之外,我们也可以根据自己的实际情况来自己配置我们所需要的线程池。

    以下是四种线程池的使用方法:

    Runnable runnable - new Runnable(){    @override    public void run(){        SystemClock.sleep(3000);    }}//fixedThreadPoolExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);fixedThreadPool.execute(runnable);//cachedThreadPoolExecutorService cachedThreadPool = Executors.newCachedThreadPool();cachedThreadPool.execute(runnable);//scheduledThreadPoolScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);//2000ms之后执行scheduledThreadPool.schedule(runnable,2000,TimeUnit,MILLISECONDS);//延迟10ms之后,每隔100ms执行一次scheduledThreadPool.scheduleAtFixedRate(runnable,10,100,TimeUnit.MILLISECONDS);//singleThreadPoolExecutorService singleThreadPool = Executors.newSingleThreadExecutor();singleThreadPool.executor(runnable);

    另:记录几个相关的小知识点。
    1.AsyncTask几个注意事项:

    1. AsyncTask对象必须在主线程中创建。
    2. execute方法必须在主线程中调用。
    3. 一个AsyncTask任务只能被执行一次。
    4. AsyncTask在1.6之前是串行的,1.6之后开始并行执行,3.0之后为了防止并发错误,又开始串行,当我们可以通过executeOnExecutor方法来并发执行任务,但该方法是3.0新加的,3.0之前不兼容。

    2.HandlerThread
    HandlerThread继承自Thread,在run方法中通过Looper.prepare方法来创建了一个消息队列并开始循环,ThreadHandler中的run方法是一个无限循环,当使用完毕之后需要通过quit或者quitSafety方法退出。

    3.IntentService
    IntentService是一种特殊的Service,我们知道,service主要用于执行后台操作,但service又运行在主线程中,不能进行耗时操作,而Thread可以进行耗时操作,但是优先级比较低,受限制较多,而IntentService综合了二者的优势,比较适合执行一些优先级比较高的后台耗时任务。

    IntentService封装了HandlerThread和Handler,可以在IntentService中的onCreate源码中看到。

    当IntentService第一次启动的时候,onCreate方法执行,其中创建了HandlerThread,然后使用HandlerThread的Looper对象创建了一个Handler对象,这样,通过该Handler对象发送的消息都会在HandlerThread中去执行,这样就将任务切换到了子线程中去执行。

    而当每次启动IntentService的时候,在onStartCommand方法中会将intent对象传递给handler对象,发送一个消息,在HandlerThread中执行任务,当所有的任务都执行完毕之后,intentService会通过stopSelf方法来停止服务。

    IntentService的onHandlerIntent方法是一个抽象方法,需要我们在子类中去实现它,用以区分intent去执行具体的任务,当目前只有一个任务存在的时候,onHandlerIntent执行完这个任务的时候,stopSelt会直接停止服务,如果目前存在多个任务的时候,当onHandlerIntent处理完所有的任务之后,stopSelf才会停止服务。

    另外,由于没执行一个后台任务就会启动一次IntentService,而IntentService内部是由handler来向HandlerThread发送请求的,handler中的Looper是顺序处理任务的,所以,这就意味着IntentService中的任务也是顺序来执行的。当有多个任务的时候,这些任务将会按照外界发起请求的顺序来执行。

参考资料:Android开发艺术探索

0 0