线程池应对于突然增大、来不及处理的请求,无非两种应对方式: 将未完成的请求放在队列里等待 临时增加处理线程,等高峰回落后再结束临时线程JDK的Executors.newFixedPool() 和newCachedPool(),分别使用了这两种方式。 不过,这俩函数在方便之余,也屏蔽了ThreadPool原本多样的配置,对一些不求甚解的码农来说,就错过了一些更适合自己项目的选择。 1. ThreadPoolExecutor的原理 经典书《Java Concurrency in Pratice(Java并发编程实战)》第8章,浓缩如下: 1. 每次提交任务时,如果线程数还没达到coreSize就创建新线程并绑定该任务。 2. 线程数达到coreSize后,新增的任务就放到工作队列里,而线程池里的线程则努力的使用take()从工作队列里拉活来干。 3. 如果队列是个有界队列,又如果线程池里的线程不能及时将任务取走,工作队列可能会满掉,插入任务就会失败,此时线程池就会紧急的再创建新的临时线程来补救。 4. 临时线程使用poll(keepAliveTime,timeUnit)来从工作队列拉活,如果时候到了仍然两手空空没拉到活,表明它太闲了,就会被解雇掉。 5. 如果core线程数+临时线程数 >maxSize,则不能再创建新的临时线程了,转头执行RejectExecutionHanlder。默认的AbortPolicy抛 RejectedExecutionException异常,其他选择包括静默放弃当前任务(Discard),放弃工作队列里最老的任务 (DisacardOldest),或由主线程来直接执行(CallerRuns),或你自己发挥想象力写的一个。 2. FixedPool 与 CachedPool FixedPool默认用了一条**的工作队列 LinkedBlockingQueue, 所以只去到上面的第2步就不会继续往下走了,coreSize的线程做不完的任务不断堆积到无限长的Queue中,所以只有coreSize一个参数,其他maxSize,keepAliveTime,RejectHandler的配置都不会实际生效 CachedPool则把coreSize设成0,然后选用了一种特殊的Queue --SynchronousQueue,只要当前没有空闲线程,Queue就会立刻报插入失败,让线程池增加新的临时线程,默认 KeepAliveTime是1分钟,而且maxSize是整形的最大值,也就是说只要有干不完的活,都会无限增增加线程数,直到高峰过去线程数才会回落。 3. 对FixedPool的进一步配置 3.1 设置QueueSize 此时,最好还是把maxSize设为coreSize一样的值,不把临时线程及其keepAlive时间拉进来,Queue+临时线程两者结合听是好听,但很难设置好。 4. 对CachedPool的进一步配置 4.1 设置coreSize coreSize默认为0,但很多时候也希望是一个类似FixedPool的固定值,能处理大部分的情况,不要有太多加加减减的波动,等待和消耗的精力。 4.2 设置maxSize及rejectHandler同理,maxSize默认是整形最大值,但太多的线程也很可能让系统崩溃,所以建议还是设一下maxSize和rejectHandler。 4.3 设置keepAliveTime默认1分钟,可以根据项目再设置一把。 5. SpringSide的ThreadPoolBuilder 广告时间,SpringSide的ThreadPoolBuilder能简化上述的配置。 此文太科普,不是为了帮SpringSide里的Utils打广告也不会写 |
小黑屋|在路上
( 蜀ICP备15035742号-1 )
GMT+8, 2025-7-9 14:35
Copyright 2015-2025 djqfx