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

 

 

posted @ 2021-04-10 15:50  良工说技术  阅读(767)  评论(0编辑  收藏  举报