先简单聊一下快速创建常用的四个线程池,预备知识,当然面试题也会考啦:
1.newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
2.newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
3.newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
4.newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
先来看下用线程池处理任务的时候的场景:
线程池里有我们的线程和一个队列,线程又分核心线程和非核心线程(空闲超出一定时间会被消灭),队列的话就是如果核心线程创建满了,就把任务放到队列中,如果队列也放满了,那就再创建非核心线程,那如果非核心线程也创建满了呢?那就涉及到拒绝策略了,在后面会讲。
abcc8e405d1d4b3c384ad56cc8d28745_1539943510290485858e761.jpg
再来看看ThreadPoolExecutor的构造方法,有好多,就直接看最多参数的那个吧
x.png
}X(Q~ZTZ2_TYA`LF}@V_XTG.png
corePoolSize 线程池的基本大小, 当提交一个任务到线程池的时候,线程池会创建一个线程来执行任务,即使当前线程池已经存在空闲线程,仍然会创建一个线程,等到需要执行的任务数大于线程池基本大小时就不再创建。
maximumPoolSizeSize 线程池最大数量,线程池允许创建的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界的任务队列这个参数就没什么效果。
keepAliveTime 线程活动保持时间,线程池的工作线程空闲后,保持存活的时间(针对非核心线程),所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
但是如果调用了allowCoreThreadTimeOut(boolean)方法(开始针对核心线程啦),在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;unit 线程活动保持时间的单位,可选择的单位有时分秒等等。
workQueue 任务队列。用来暂时保存任务的工作队列,需要是BlockingQueue的实现
threadFactory:是构造 Thread 的方法,一个接口类,可以使用默认的 default实现,也可以自己去包装和传递,主要实现 newThread 方法即可;
handler 拒绝策略,如果线程池满了,而且有界队列也满了,对新添加进来的任务的处理方式
其实这四种线程在内部也是自己实现了ThreadPoolExecutor的构造方法,一起来看看吧
newFixedThreadPool
WMWQOEH_1JJP4A~)TF(5_M3.png
看的出来什么不,核心线程和最大线程数是一样的,但是用了LinkedBlockingQueue,而且没有赋大小值,这可是个无界队列啊!任务一多全放进这队列里,分分钟OOM。
newCachedThreadPool
22.png
好了,咱们这次没有核心线程了,也用了有界队列了,但是这Integer.MAX_VALUE是什么鬼,人任务进来一个你new一个呗,线程无限创建,也要OOM。
SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
newScheduledThreadPool
OD}0}VRH}%51`Y3P885HKUJ.png
跟咱们CachedThreadPool一个毛病,虽然这个DelayedWorkQueue是有界队列(看下图,默认大小为16),但是你线程最大数是Integer.MAX_VALUE,OOM!1.png
newSingleThreadExecutor
@5`6T%~{5K179HDEU(KJ`~4.png
总算轮到最后一个了,这个好分析,还是用了无界队列的问题,任务过来就放到队列里,会导致队列特别长从而OOM。
拒绝策略
ThreadPoolExecutor.AbortPolicy:直接抛出异常
ThreadPoolExecutor.DiscardPolicy:不处理,丢弃掉
ThreadPoolExecutor.DiscardOldestPolicy:丢弃最老的。也就是说如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。因为队列是队尾进,队头出,所以队头元素是最老的,因此每次都是移除对头元素后再尝试入队。
ThreadPoolExecutor.CallerRunsPolicy:直接调用这个线程池所在的线程来运行任务(如果线程池在main方法创建的话,任务就交给main方法)
发现网上已经有比较好的博客,而且比较好理解,就不重复造轮子啦
线程池的RejectedExecutionHandler(拒绝策略)
网友评论