Android-Threadpool

     关于线程我不想再说什么,感兴趣的同学可以看我之前写过的一篇文章:Android-多线程,这里对线程有一个比较详细的解释。

Android-多线程 - 简书

我们今天直入重点:聊意料我们常用的线程池.

一:

1.什么是线程池?

听名字也不难理解,线程池就是线程的集合,用来控制管理线程,控制并发数等,减少了线程创建和销毁的次数。提高线程的复用率,降低性能的损耗。

2.有哪些线程池呢?

        首先我们先从源码的角度开始分析,先创建一个线程池,然后一步步查看它的源码,分析其关系。

        直接上图:


6748497-85b62b844966a02b.png

然后一步步查看:从最上面的接口开始。


6748497-af2cdf4eda9ba311.png

首先。Executor是一个类的实现接口,里面有一个执行方法:execute().,这就是线程池的起源,属于线程的一个执行工具,并不是真正的线程池。


6748497-f086be03b4b02e1e.png

如图,我们可以看到ExecutorService接口继承自我们之前的Executor工具类,它里面有好多的api方法,是真正的线程池接口。


6748497-5c60156d1c109416.png

然后呢,AbstractExecutorService是一个抽象方法,它实现了ExecutorService接口,可以实现其抽象方法, 也可以进行自定义。


6748497-1aae663be8b74498.png

最后ThreadPoolExecutor才是我们的具体实现类,Android中的线程池都是 直接或间接通过配置ThreadPoolExecutor 来实现不同的线程池。

我们来看一下它的构造方法:


6748497-d3b834444b1b45c0.png


  其实ThreadPoolExecutor有四种构造方法,我们举例其中最多的一个讲一下。

(1)  //核心线程数 

int corePoolSize,  

注:线程池新建线程的时候,如果当前线程总数小于 corePoolSize ,则新建的是核心线程;如果超过corePoolSize,则新建的是非核心线程。

(2)  //最大线程数,活动线程数量超过它,后续任务就会自动排队                    

int maximumPoolSize

线程总数= 核心线程数 + 非核心线程数。

(3)    //超时时长,非核心线程如果长时间闲置,超过这个时长便被回收    ,但如果设置allowCoreThreadTimeOut = true,则会作用于核心线程 ,超过时长也会被回收。  

long keepAliveTime,                            

(4)   //枚举类型,设置keepAliveTime的单位,MILLISECONDS : 毫秒 、SECONDS : 秒、MINUTES : 分、HOURS : 小时、DAYS : 天

TimeUnit unit,  

//缓冲任务队列,线程池的execute方法会将Runnable对象存储起来,当所有的核心线程都有活干,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。

BlockingQueue workQueue,  

(5)  //线程工厂接口,只有一个new Thread(Runnable r)方法,可以认为是线程池创建新线程  

BlockingQueue有四种队列类型:(1)SynchronousQueue:(同步队列)

                                                    (2)LinkedBlockingQueue(链表阻塞队列)

                                                    (3)ArrayBlockingQueue(数组阻塞队列)

                                                    (4)DelayQueue(延迟队列)

SynchronousQueue:(同步队列)这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,但如果所有的线程都在工作呢?这种情况下,SynchronousQueue就会新建一个线程来处理这个任务。所以为了保证不出现(线程数达到了maximumPoolSize而不能新建线程)的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大,去规避这个使用风险。

LinkedBlockingQueue(链表阻塞队列):这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize

ArrayBlockingQueue(数组阻塞队列):可以限定队列的长度(既然是数组,那么就限定了大小),接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

DelayQueue(延迟队列):队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

(6)   //创建线程的方式

       ThreadFactory threadFactory  这是一个接口,new它的时候需要实现他的Thread newThread(Runnable r)方法。

(7)   // 这个主要是用来抛异常的,如果线程无法执行新任务一般会抛一个RejectedExecutionException异常。

       RejectedExecutionHandler handler

二:ok,讲完了这些,我们来看看如何使用线程池?


6748497-bac59a3c6f27a677.png

线程池新添加了任务,那么线程池是如何运行的呢,总结:

1:如果线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务

2:如果线程数量达到了corePools,则将任务移入队列等待

3:如果队列已满,新建线程(非核心线程)执行任务

4:如果队列已满,总线程数又达到了maximumPoolSize,就会由RejectedExecutionHandler抛出异常


但我们不需要这么费劲,java已经给我们提供了四种线程池,,来搞一下。

第一种:

6748497-513386b17d9f3308.png

A:定长线程池  newFixedThreadPool ,只有核心线程,数量固定,不会被回收。可控制线程最大并发数,超出的线程会在队列中等待。其中nThreads参数是我们要创建几个线程。如果所有线程都出于运行状态,提交额外的任务,他们会在队列中等待,直到有一个线程可用为止。

使用:


6748497-77bf750d842024cb.png

第二种:

6748497-e37a5e402395ca44.png

B:缓存线程池 newCacheThreadPool,只有非核心线程,最大线程数不做限制,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。空闲线程超过60秒会被终止并从线程池里移除缓存。

使用:


6748497-88d5d31451a4c618.png

第三种:

6748497-112b15782c909b90.png

C:定长任务线程池,ScheduledExecutorService.支持定时及周期性任务执行

corePoolSize 参数是指在池中保留的线程数,即使它们是空闲的。这个函数最终会返回一个新创建的调度线程池.

使用:


6748497-986da8dc7c1185fa.png

第四种:

6748497-13c3b48038240a35.png

    D:newSingleThreadExecutor 创建一个单线程的线程池,用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个单独的线程终止是因为在执行前异常或者终止,若需要执行后续的任务,那么就需要一个新的去替代它,返回的是一个重新创建的单线程去执行。

使用:

6748497-fa24adbb307c7cf2.png

突然觉得直接写用法,不来个demo,是不是小伙伴不容易理解呀。那好。我们以缓存线程池为例.来个场景演示。大家都知道下载文件吧,我们就整这个。多线程下载多个文件。

    上一波图:

        咳咳,初始化UI就.......省了吧。

6748497-b06f4f63e2bdec1d.png


6748497-0160460e3dff1d3a.png


6748497-7e6fbbf4f1669727.png


6748497-f68adfbda57a9f76.png

效果:


6748497-b18e66d00dce2c65.png

点击下载文件可以看到是同一个线程池,两个线程平行。


6748497-25da754868dba045.png

文件A和文件C是同一个线程在下载,也就是在线程thread-1下载文件A完毕后,点击下载文件C,并没有在重新在创建线程,而是复用了文件A下载的thread-1继续下载文件C。

好了就这么多,拜拜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛之涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值