java面试一日一题:java线程池
问题:请讲下java中的线程池
分析:在面试中经常问到线程池的问题,要掌握其基本概念,使用方法,注意事项等,引申下tomcat中默认的线程数是多少
回答要点:
主要从以下几点去考虑,
1、为什么要使用线程池
2、线程池的基本参数
3、为什么不使用java提供的线程池,而是使用自己创建
4、如何设置线程数大小;
5、线程池在tomcat中的使用;
为什么要使用线程池
在日常的开发过程中,经常要用到多线程,那么为什么不直接新建一个线程,而是选择使用线程池那,因为线程的创建要消耗系统资源,占用CPU的时间,所以考虑使用线程池;
线程池的基本参数
Java提供了线程池类ThreadPoolExecutor,该类是线程池的基类,有以下参数
corePoolSize 核心线程数
maximumPoolSize 最大线程数
keepAliveTime 线程数超过核心线程数后,多余的线程的空闲时间
unit 上面的参数的单位
workQueue 阻塞队列
threadFactory 创建线程的工厂
handler 拒绝策略
一个任务被提交到线程池的过程:
从上面的流程图中可以引申出以下几个问题,核心线程数要怎么设置;阻塞队列有几种;拒绝策略有几种;
先看阻塞队列及决策策略
阻塞队列
常用的阻塞队列有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、priorityBlockingQueue
ArrayBlockingQueue 底层使用数组+ReeTrantLock实现,既然是数组实现那么必然就要指定数组的长度
LinkedBlockingQueue 使用单链表实现,最大长度为Integer.MAX_VALUE
SynchronousQueue 要将任务放入SynchronousQueue中必须有一个线程在等待消费该任务,如果没有线程在等待,那么线程池中线程数小于最大线程数就会创建一个线程,否则会执行拒绝策略
PriorityBlockingQueue 具有优先级的阻塞队列
拒绝策略
ThreadPoolExecutor实现了4中拒绝策略,CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy
CallerRunsPolicy 调用者来执行该任务;
AbortPolicy 抛出异常,开发者可以捕获该异常,默认的策略;
DiscardPolicy 直接丢弃,什么也不做;
DiscardOldestPolicy 丢弃队列中最老的任务,就是最先入队的任务,也即将要被执行的任务;
java提供的线程池
Java的Exectors类提供了一些线程池的方法供开发者来用,但是最好不用Java提供的,下面看有哪些以及为什么不建议使用
newFixedThreadPool() 固定线程数的线程池,核心线程数=最大线程数,使用LinkedBlockingQueue,拒绝策略是默认的AbortPolicy;适用于为了满足资源管理的要求,而限制线程数量,适用于负载较重的机器
newSingleThreadExecutor() 只有一个线程的线程池,核心线程数=最大线程数=1,使用LinkedBlockingQueue,拒绝策略是默认的AbortPolicy;适用于顺序执行各个任务的场景
newCachedThreadPool() 按需创建新的线程池,核心线程数=0,最大线程数为Integer.MAX_VALUE,阻塞队列为SynchronousQueue,拒绝策略是默认的AbortPolicy,线程池可以无限扩展,当任务多时创建许多线程,任务少时,自动清空线程;适应于执行短期异步任务,或者负载较轻的机器
newScheduledThreadPool() 创建一个延时或定时的线程池,最大线程数为Integer.MAX_VALUE,阻塞队列为DelayedWorkQueue
如何设置线程池中线程的数量
要想设置合理的线程数,需要区分任务是计算(CPU)密集型还是IO密集型
计算密集型 针对计算密集型一般设置为CPU核数+1比较合理,因为是计算密集型,那么计算就要用CPU,设置为CPU的核数可以充分利用CPU的优势,至于为什么要加1,可以理解为计算密集型,也要有IO操作,加1是为了在等待IO的时候,充分利用CPU
IO密集型 对应IO密集型,也就是说该任务涉及很多的IO操作,比如读写磁盘,远程RPC调用等,网上有很多数是设置为2*CPU核数+1,当然这也是可以的,不过下面这样设置可能更能发挥CPU的性能,CPU数*CPU利用率*(任务等待时间/任务计算时间+1),假如有个8核机器,任务有100ms在计算,800ms在等待IO,CPU的利用率为100%的话,线程数=8*1*(800/100+1)=72,当然现实场景中CPU的利用率不可能飙到100%,还要具体场景具体分析;
线程池在tomcat中的使用
tomcat作为servlet服务器,要处理请求,是为每个请求创建一个线程,那么它的配置是在哪里那,在tomcat的conf/server.xml文件中
看上面有个Executor的配置实例,maxThreads的配置,看上图中例子给出的值为150。其实tomcat7/8默认的线程池大小为200.
参考:https://joonwhee.blog.csdn.net/article/details/106609583