通常状况有下面三个缘由java
为何会影响处理效率呢?
答:线程的建立须要开辟虚拟机栈、本地方法栈、程序计数器等线程私有的内存空间; 在线程销毁时须要回收这些系统资源.频繁地建立和销毁线程会浪费大量的系统资源,增长并发编程风险.android
Android中线程池来自于Java,那么研究Android线程池其实也能够说是研究Java中的线程池git
在Java中,线程池的概念是Executor这个接口,具体实现为ThreadPoolExecutor类,学习Java中的线程池,就能够直接学习他了面试
对线程池的配置,就是对ThreadPoolExecutor构造函数的参数的配置,既然这些参数这么重要,就来看看构造函数的各个参数吧数据库
当全部的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,若是队列满了,则新建非核心线程执行任务编程
经常使用的workQueue类型:缓存
SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,若是全部线程都在工做怎么办?那就新建一个线程来处理这个任务!因此为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize通常指定成Integer.MAX_VALUE,即无限大安全
LinkedBlockingQueue:这个队列接收到任务的时候,若是当前线程数小于核心线程数,则新建线程(核心线程)处理任务;若是当前线程数等于核心线程数,则进入队列等待。因为这个队列没有最大值限制,即全部超过核心线程数的任务都将被添加到队列中,这也就致使了maximumPoolSize的设定失效,由于总线程数永远不会超过corePoolSizebash
ArrayBlockingQueue:能够限定队列的长度,接收到任务的时候,若是没有达到corePoolSize的值,则新建线程(核心线程)执行任务,若是达到了,则入队等候,若是队列已满,则新建线程(非核心线程)执行任务,又若是总线程数到了maximumPoolSize,而且队列也满了,则发生错误网络
DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
maxPoolSize:最大线程数
keepAliveTime:线程空闲时间 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize 若是allowCoreThreadTimeout=true,则会直到线程数量=0 方法allowCoreThreadTimeout()能够设置容许核心线程超时退出
TimeUnit :keepAliveTime的单位,TimeUnit是一个枚举类型,其包括:
ThreadFactory 建立线程的方式,是一个接口,new他的时候须要实现他的Thread
rejectedExecutionHandler:任务拒绝处理器
ThreadPoolExecutor类有几个内部实现类来处理这类状况:
线程池按如下行为执行任务
面试时可能常常会被问到的一个问题
通常状况下有两种方式能够向线程池中提交任务
execute 调用execute()方法提交任务到线程池中进行执行,这个方法是没有返回值的,比较经常使用,是ThreadPoolExecutor本身实现的方法;这种方式没法判断任务被线程池执行的状况,好比是否成功;
submit 调用submit方法会有返回,完整的方法描述是:public Future<?> submit(Runnable task),其中泛型T是咱们回调的结果类型,若是咱们但愿监放任务执行的结果,可使用submit方法提交任务,这个方法实际上是ThreadPoolExecutor的父类AbstractExecutorService的方法;
返回的future可经过get()获取返回值,get()会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后当即返回,这时候可能任务没有执行完.
通常咱们可能不须要本身去根据实际须要进行参数设定对应的ThreadPoolExecutor来实现线程池,大部分状况下,咱们使用java并发包中提供的Executors的类调用它的静态方法获取须要的线程池类型
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
复制代码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
复制代码
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
复制代码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
复制代码
可经过调用线程池的shutdown或shutdownNow方法来关闭线程池.
它们的原理是遍历线程池中的工做线程,而后逐个调用线程的interrupt方法来中断线程,因此没法响应中断的任务可能永远没法终止.
可是它们存在必定的区别
只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true.
当全部的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true.
至于应该调用哪种方法,应该由提交到线程池的任务的特性决定,一般调用shutdown方法来关闭线程池,若任务不必定要执行完,则能够调用shutdownNow方法.
要想合理地配置线程池,就必须首先分析任务特性,可从如下几个角度来分析
使用不一样规模的线程池进行处理
经过Runtime.getRuntime().availableProcessors()方法得到当前设备的CPU个数
优先级不一样的任务可使用PriorityBlockingQueue处理.它可让优先级高 的任务先执行. 若是一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行,这一点须要特别注意
执行时间不一样的任务能够交给不一样规模的线程池来处理,或者可使用优先级队列,让执行时间短的任务先执行.
建议使用有界队列 有界队列能增长系统的稳定性和预警能力,若是咱们设置成无界队列,那么线程池的队列就会愈来愈多,有可能会撑满内存,致使整个系统不可用,而不仅是后台任务出现问题.固然这些对于java后台的开发比较关键,对于android的开发相对比较少一些;
若是在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题时,能够根据线程池的使用情况快速定位问题.可经过线程池提供的参数进行监控,在监控线程池的时候可使用如下属性:
taskCount:线程池须要执行的任务数量
completedTaskCount:线程池在运行过程当中已完成的任务数量,小于或等于taskCount。
largestPoolSize:线程池里曾经建立过的最大线程数量.经过这个数据能够知道线程池是否曾经满过.如该数值等于线程池的最大大小,则表示线程池曾经满过.
getPoolSize:线程池的线程数量.若是线程池不销毁的话,线程池里的线程不会自动销毁,因此这个大小只增不减.
getActiveCount:获取活动的线程数.
复制代码
经过扩展线程池进行监控.能够经过继承线程池来自定义线程池,重写线程池的 beforeExecute、afterExecute和terminated方法,也能够在任务执行前、执行后和线程池关闭前执行一些代码来进行监控.例如,监控任务的平均执行时间、最大执行时间和最小执行时间等.
往期文章推荐
那些年你曾经工做过的奇葩公司能有多奇葩!
Kotlin扩展和对应的java代码解析
git操做高级命令
了解一下计算机网络层吧
计算机网络基础
带你深刻了解运输层
看看网络安全的一些知识吧!
是时候了解一下应用层的知识了一块儿来吧!
如何使用命令行卸载手机预知APP
长按二维码关注公众号,接收新的消息推送,值得期待哟!感谢您的支持!