操作系统中,线程是操作系统调度的最小单位,同时线程又是一种受限的系统资源,线程不可能无限制的产生,并且线程的创建和销毁都有相应的开销。当系统存在大量线程时,系统通过时间片轮转的方式调度每个线程,因此线程不可能做到绝对的并行,除非线程数量小于CPU的核心数。
使用多线程的目的:尽可能多的使用CPU的资源,让效率最大化。多个线程完成多项任务,提高系统的效率。当线程数量越来越多时,操作系统将会花费更多时间去理清线程之间的关系,所以需要使用线程池。
线程池:初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程(提高线程复用,减少性能开销)。
使用线程池的原因:减少创建和销毁线程的次数,每个线程都可以被重复利用,节约应用内存
1. 线程池改进了一个应用程序的响应时间。复用已有线程而不是新建线程。
2. 线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。
3.线程池根据当前在系统中运行的进程来优化线程时间片。
4. 线程池允许我们开启多个任务而不用为每个线程设置属性。
5. 线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。
6. 线程池可以用来解决处理一个特定请求最大线程数量限制问题。有效控制线程池的最大并发数,避免大量线程之间互相抢占系统资源而导致阻塞的现象
7.能够对线程简单的管理,定时执行,间隔循环执行等
Executor是一个接口,真正实现类为ThreadPoolExecutor,所提供的四类线程池通过Executor所提供的工厂方法而得到。
构造方法:public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory)
corePoolSize:
线程的核心线程数。
默认情况下,核心线程数会在线程中一直存活,即使它们处于闲置状态。
如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么核心线程就会存在超时策略,这个时间间隔有keepAliveTime所决定,当等待时间超过keepAliveTime所指定的时长后,核心线程就会被停止。
maximumPoolSize
线程池所能容纳的最大线程数。
当活动线程数达到这个数值后,后续的新任务将会被阻塞。
keepAliveTime
非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收,当ThreadPoolExector的allowCoreThreadTimeOut属性设置为True时,keepAliveTime同样会作用于核心线程。
unit
用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等。
TimeUnit.NANOSECONDS 纳秒
TimeUnit.MICROSECONDS 微秒
TimeUnit.MILLISECONDS 毫秒
TimeUnit.SECONDS 秒
TimeUnit.MINUTES 分钟
TimeUnit.HOURS 小时
TimeUnit.DAYS 天
workQueue
线程池中的任务队列,通过线程池execute方法提交的Runnable对象会存储在这个参数中。
这个任务队列是BlockQueue类型,属于阻塞队列,就是当队列为空的时候,此时取出任务的操作会被阻塞,等待任务加入队列中不为空的时候,才能进行取出操作,而在满队列的时候,添加操作同样被阻塞。
threadFactory
线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,它只有一个方法newThread(Runnable r),用来创建线程。
RejectedExecutionHandler
当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。
submit()和execute()的区别
submit()其实还是需要调用execute()去执行任务,而submit()和execute()本质上的不同是submit()将包装好的任务进行了返回.
ThreadPoolExecutor执行任务逻辑规则:
1:如果线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务
2:如果线程数量达到了corePools,则将任务移入队列等待
3:如果队列已满,新建线程(非核心线程)执行任务
4:如果队列已满,总线程数又达到了maximumPoolSize,就会由RejectedExecutionHandler抛出异常
四种常用线程池
1.newFixedThreadPool
该模式全部由核心线程去实现,并不会被回收,没有超时限制和任务队列的限制,会创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。实现代码如下:
public static ExecutorService newFixedThreadPool(int mThreads){
return new ThreadPoolExecutor(mThreads,mThreads,0L,TimeUtil.MILLISECONDS,new LinkedBlockingQueue());
}
2.newCachedThreadPool
该模式下线程数量不定的线程池,只有非核心线程,最大值为Integer.MAX_VALUE,会创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。实现代码如下:
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUtil.SECONDS,new SynchronousQueue());
}
3.newScheduledThreadPool
该模式下核心线程是固定的,非核心线程没有限制,非核心线程闲置时会被回收。会创建一个定长线程池,执行定时任务和固定周期的任务。实现代码如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new SchduledThreadPoolExecutor(corePoolSize)
}
public SchduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue());
}
4.newSingleThreadExecutor
该模式下线程池内部只有一个线程,所有的任务都在一个线程中执行,会创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。实现代码如下:
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService(newThreadPoolExecutor(1,1,
0L,TimeUtil.MILLISECONDS,new LinkedBlockingQueue()));
}
这四种线程池都有OOM风险
1. FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;
2. CachedThreadPool 和 ScheduledThreadPool : 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
参考:安卓开发艺术探索 鸿洋公众号,阿里巴巴android开发规范等