1、线程池的核心属性有哪些
- newFixedThreadPool:固定大小的线程池,每次提交一个任务就创建一个线程,直到达到最大线程数。一旦达到最大池就不会改变,如果有一个线程执行异常结束,线程池会补充一个新线程。
- newCachedThreadPool:创建一个可缓存的线程池。如果线程池大小超过需要处理线程的数量,那么就会回收空闲的线程。任务增加时,线程池会创建新的线程。线程池不会限制数量大小,完全依赖于JVM能够创建的最大线程大小。
- newSingledThreadExecutor: 创建一个单线程话的线程池,用唯一的工作线程来工作。
- newSceduledThreadPool:创建一个大小无限的线程池,支持定时/周期性任务执行。
newFixedThreadPool的参数具体值?
比如创建一个线程数为N的线程池,那么核心线程数和最大线程数都为N,keepAliveTime为0,因为不需要等待,阻塞队列是linkedblockedqueue,默认大小为Integer.MAX_VALUE最大值,如果往阻塞队列中一直添加任务会导致OOM。
2、线程池线程个数确定
在多线程编程的时候,一般线程数会多于CPU的数量,但是同一时刻一个CPU上只能有一个线程去执行,所以就要靠不断地进行上下文切换。
而线程确定的数量有两个常用的公式
CPU密集型任务的话就是N+1:这种情况主要是消耗CPU资源,可以将线程数设置为N(CPU核心数+1),比CPU核心数多出来的1是为了防止线程偶发的缺页中断,或者其他原因导致的任务暂停而带来的影响。一旦任务暂停,CPU就会处于空闲状态,而在这种情况下,多出来的线程就会充分利用CPU的资源。
IO密集型任务(2N):这种任务应用起来,系统的大部分时间就处理IO操作,而线程在处理IO操作的时候不会占用CPU资源,所以这段时间CPU可以处理其他线程,因此在IO密集型的任务中可以创建更多的线程,一般为2N。
3、线程池的优点
- 降低资源消耗,重用存在的线程,减少对象创建销毁的开销。
- 提高响应速度,有效控制最大的并发数,提高系统资源利用率,同时避免过多资源竞争,避免阻塞。
- 提高线程的可管理性。线程是稀缺资源,如果无限制被创建会消耗资源,影响稳定性。使用线程池可以统一的分配,调优和监控。
4、线程池不允许使用 Executors 去创建,要通过 ThreadPoolExecutor的方式原因?
Executors创建线程会有一些问题
(1)newFixedThreadPool和newSingledThreadExecutor:
队列使用LinkedBlockingQueue,队列长度为Integer.MAX_VALUE,可能造成堆积,导致OOM
(2)newScheduledThreadPool和newCachedThreadPool:
线程池里面允许最大的线程数是Integer.MAX_VALUE,可能会创建过多的线程,导致OOM。
5、ThreadPoolExecutor构造函数里的参数,解释一下各个参数的作用
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:核心线程数,线程池里维护的最少线程数,会一直存活,定义了最小的可以同时执行的线程数量。(在刚创建线程池时不会立即启动,等有任务提交才逐渐创建线程直到达到核心线程数)
- maximumPoolSize:线程池维护的最大线程数量,超过的话就会被阻塞。(首先要达到核心线程数,且阻塞队列也满了,如果没有达到最大线程数量,再创建新线程。)
- keepAliveTime:非核心线程的闲置超时时间。
- unit:keepAliveTime时间的单位。
- workQueue:线程池的任务队列,新来的任务会判断正在运行的线程是否达到核心线程数,达到的话就先存放到任务队列。
- threadFactory:创建新线程使用的工厂
- handler:当线程池中线程的数量超过最大线程数,就有拒绝任务的处理策略。
ThreadPoolExecutor饱和策略:当前同时执行的线程数超过了最大线程数量并且任务队列也已经放满的时候,定了4中拒绝策略。
拒绝策略:
- AbortPolicy:中止策略:直接抛出异常。
- DiscardPolicy:抛弃策略,什么都不做,直接抛弃被拒绝的策略
- DiscardOldestPolicy:抛弃最老策略。抛弃阻塞队列中最老的队列。
6、线程池的执行流程
首先提交任务–是否达到核心线程数-未达到就创建工作线程-已达到看任务队列是否满-没的话就存入等待队列-满的话是否达到最大线程数-没就创建工作线程,有就执行拒绝策略。