你都理解创建线程池的参数吗?
多线程可以说是面试官最喜欢拿来问的题目之一了,可谓是老生之常谈,不管你是新手还是老司机,我相信你一定会在面试过程中遇到过有关多线程的一些问题。那我现在就充当一次面试官,我来问你:
现有一个线程池,参数corepoolsize = 5,maximumpoolsize = 10,blockingqueue阻塞队列长度为5,此时有4个任务同时进来,问:线程池会创建几条线程?
如果4个任务还没处理完,这时又同时进来2个任务,问:线程池又会创建几条线程还是不会创建?
如果前面6个任务还是没有处理完,这时又同时进来5个任务,问:线程池又会创建几条线程还是不会创建?
如果你此时一脸懵逼,请不要慌,问题不大。
创建线程池的构造方法的参数都有哪些?
要回答这个问题,我们需要从创建线程池的参数去找答案:
java.util.concurrent.threadpoolexecutor#threadpoolexecutor:
public threadpoolexecutor(int corepoolsize, int maximumpoolsize, long keepalivetime, timeunit unit, blockingqueue<runnable> workqueue, threadfactory threadfactory, rejectedexecutionhandler handler) { if (corepoolsize < 0 || maximumpoolsize <= 0 || maximumpoolsize < corepoolsize || keepalivetime < 0) throw new illegalargumentexception(); if (workqueue == null || threadfactory == null || handler == null) throw new nullpointerexception(); this.acc = system.getsecuritymanager() == null ? null : accesscontroller.getcontext(); this.corepoolsize = corepoolsize; this.maximumpoolsize = maximumpoolsize; this.workqueue = workqueue; this.keepalivetime = unit.tonanos(keepalivetime); this.threadfactory = threadfactory; this.handler = handler; }
创建线程池一共有7个参数,从源码可知,corepoolsize和maximumpoolsize都不能小于0,且核心线程数不能大于最大线程数。
下面我来解释一下这7个参数的用途:
corepoolsize
线程池核心线程数量,核心线程不会被回收,即使没有任务执行,也会保持空闲状态。
maximumpoolsize
池允许最大的线程数,当线程数量达到corepoolsize,且workqueue队列塞满任务了之后,继续创建线程。
keepalivetime
超过corepoolsize之后的“临时线程”的存活时间。
unit
keepalivetime的单位。
workqueue
当前线程数超过corepoolsize时,新的任务会处在等待状态,并存在workqueue中,blockingqueue是一个先进先出的阻塞式队列实现,底层实现会涉及java并发的aqs机制,有关于aqs的相关知识,我会单独写一篇,敬请期待。
threadfactory
创建线程的工厂类,通常我们会自顶一个threadfactory设置线程的名称,这样我们就可以知道线程是由哪个工厂类创建的,可以快速定位。
handler
线程池执行拒绝策略,当线数量达到maximumpoolsize大小,并且workqueue也已经塞满了任务的情况下,线程池会调用handler拒绝策略来处理请求。
系统默认的拒绝策略有以下几种:
- abortpolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
- discardpolicy:直接抛弃不处理。
- discardoldestpolicy:丢弃队列中最老的任务。
- callerrunspolicy:将任务分配给当前执行execute方法线程来处理。
我们还可以自定义拒绝策略,只需要实现rejectedexecutionhandler接口即可,友好的拒绝策略实现有如下:
- 将数据保存到数据,待系统空闲时再进行处理
- 将数据用日志进行记录,后由人工处理
现在我们回到刚开始的问题就很好回答了:
线程池corepoolsize=5,线程初始化时不会自动创建线程,所以当有4个任务同时进来时,执行execute方法会新建【4】条线程来执行任务;
前面的4个任务都没完成,现在又进来2个队列,会新建【1】条线程来执行任务,这时poolsize=corepoolsize,还剩下1个任务,线程池会将剩下这个任务塞进阻塞队列中,等待空闲线程执行;
如果前面6个任务还是没有处理完,这时又同时进来了5个任务,此时还没有空闲线程来执行新来的任务,所以线程池继续将这5个任务塞进阻塞队列,但发现阻塞队列已经满了,核心线程也用完了,还剩下1个任务不知道如何是好,于是线程池只能创建【1】条“临时”线程来执行这个任务了;
这里创建的线程用“临时”来描述还是因为它们不会长期存在于线程池,它们的存活时间为keepalivetime,此后线程池会维持最少corepoolsize数量的线程。
为什么不建议使用executors创建线程池?
jdk为我们提供了executors线程池工具类,里面有默认的线程池创建策略,大概有以下几种:
- fixedthreadpool:线程池线程数量固定,即corepoolsize和maximumpoolsize数量一样。
- singlethreadpool:单个线程的线程池。
- cachedthreadpool:初始核心线程数量为0,最大线程数量为integer.max_value,线程空闲时存活时间为60秒,并且它的阻塞队列为synchronousqueue,它的初始长度为0,这会导致任务每次进来都会创建线程来执行,在线程空闲时,存活时间到了又会释放线程资源。
- scheduledthreadpool:创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于timer。
用executors工具类虽然很方便,我依然不推荐大家使用以上默认的线程池创建策略,阿里巴巴开发手册也是强制不允许使用executors来创建线程池,我们从jdk源码中寻找一波答案:
java.util.concurrent.executors:
// fixedthreadpool public static executorservice newfixedthreadpool(int nthreads) { return new threadpoolexecutor(nthreads, nthreads, 0l, timeunit.milliseconds, new linkedblockingqueue<runnable>()); } // singlethreadpool public static executorservice newsinglethreadexecutor() { return new finalizabledelegatedexecutorservice (new threadpoolexecutor(1, 1, 0l, timeunit.milliseconds, new linkedblockingqueue<runnable>())); } // cachedthreadpool public static executorservice newcachedthreadpool() { // 允许创建线程数为integer.max_value return new threadpoolexecutor(0, integer.max_value, 60l, timeunit.seconds, new synchronousqueue<runnable>()); } // scheduledthreadpool public scheduledthreadpoolexecutor(int corepoolsize) { // 允许创建线程数为integer.max_value super(corepoolsize, integer.max_value, 0, nanoseconds, new delayedworkqueue()); }
public linkedblockingqueue() { // 允许队列长度最大为integer.max_value this(integer.max_value); }
从jdk源码可看出,executors工具类无非是把一些特定参数进行了封装,并提供一些方法供我们调用而已,我们并不能灵活地填写参数,策略过于简单,不够友好。
cachedthreadpool和scheduledthreadpool最大线程数为integer.max_value,如果线程无限地创建,会造成oom异常。
linkedblockingqueue基于链表的fifo队列,是*的,默认大小是integer.max_value,因此fixedthreadpool和singlethreadpool的阻塞队列长度为integer.max_value,如果此时队列被无限地堆积任务,会造成oom异常。
以上所述是小编给大家介绍的创建线程池参数详解整合,希望对大家有所帮助