一.为什么要使用线程池
1、线程池优于单线程
a .重用存在的线程,减少创建和删除对象的开销,性能良好。
b .有效控制最大并发线程数量,提高系统资源利用率,同时避免资源过度竞争,避免拥堵。
c .提供定时执行、定期执行、单线程、并发数控制等功能。
2、使用场景
)1)当不需要有魅力的精灵任务时。 例如,线程池可以用于处理非阻塞任务,例如,记录操作日志或向第三方服务通知不需要的信息
)如果有吸引力的精灵任务需要花费时间,也可以采用线程池技术
)3)要求同时性高的情况下,可以使用线程池技术优化处理
二、创建线程池的几种方法
1、执行
Java通过Executors提供了四种类型的线程池:
)1) newCachedThreadPool )创建可缓存的线程池,如果线程池的长度超过处理所需的长度,则灵活地复用空闲线程,如果不能复用,则创建新线程。 (无法控制线程的最大并发行数。)
)2)创建固定长度线程池以控制newFixedThreadPool )线程的最大并发行数,超出的线程在队列中等待。
(3) newScheduledThreadPool )创建定长线程池,支持定时和周期性的任务执行。
)4) newSingleThreadExecutor )创建单线程化线程池,只通过唯一的工作线程执行任务,以便所有任务按指定顺序(FIFO、LIFO、优先级)执行
2、使用ThreadPoolExecutor
三.为什么不允许使用Executors创建线程池
不允许使用《阿里巴巴Java开发手册》执行程序创建线程池
总结:
创建封装在Executors类中的线程池的方法易于使用,但由于存在局限性和风险,因此通过使用ThreadPoolExecutor类的构建方法手动创建线程池实例,并根据使用情况指定参数
因此,使用Executors类创建线程池与使用ThreadPoolExecutor类的区别在于通过使用ThreadPoolExecutor类传递给设置的线程池的参数
四.创建线程池,建议使用ThreadPoolExecutor
一、若干重要结构参数
(1) corePoolSize :
除非设置了线程超时时间,否则即使未使用这些线程,线程池中保留的线程数也是如此
)2) maximumPoolSize :
最大线程数。 workQueue队列已满,无法放置新任务,当您在execute中添加新任务时,线程池将创建新线程。 线程数大于corePoolSize,但不会超过maximumPoolSize。 如果超过maximumPoolSize,则会抛出异常,如rejecececolsize
(3) keepAliveTime和unit
如果线程池中的线程数大于workQueue,并且线程空闲时间长于keepAliveTime,则线程将被丢弃。 unit是keepAliveTime的时间单位。
(4)工作队列
阻止队列。 如果线程池中运行的线程数达到corePoolSize,则在execute中添加新任务会将其添加到workQueue队列中,并排队等待执行,而不会立即执行。
5 ) RejectedExecutionHandler提交任务数超过maximumPoolSize workQueue之和时,任务将交给RejectedExecutionHandler进行处理
其中rejectedExecutionHandler字段用于设置拒绝策略,常见的拒绝策略如下:
AbortPolicy,被拒绝任务的处理程序。 抛出RejectedExecutionException。
CallerRunsPolicy是拒绝任务的处理程序,直接在execute方法的调用线程中执行拒绝的任务。
分散性。 在任务被拒绝的处理程序中,放弃最早的未完成请求,然后重试执行。
DiscardPolicy是拒绝任务的处理程序,缺省情况下将丢弃拒绝的任务。
流程:
corePoolSize、maximumPoolSize、workQueue三者之间的关系
1 )如果线程池小于corePoolSize,则新提交的任务将创建新的线程执行任务,即使线程池中还有空闲线程。
2 )线程池达到corePoolSize后,新提交的任务将放入工作队列中,等待线程池中的任务完成
3 )当workQueue已满且变为maximumPoolSize corePoolSize时,新的提交任务将创建新的线程执行任务
4 )提交任务数较多时
过maximumPoolSize,新任务就交给RejectedExecutionHandler来处理5)当线程池中超过 corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6)当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
2、spring中如何使用ThreadPoolExecutor创建线程池
ThreadPoolTaskExecutor是一个spring的线程池技术,它是使用jdk中的java.util.concurrent.ThreadPoolExecutor进行实现。
(1)XML配置
(2)bean中使用
private ThreadPoolTaskExecutor taskExecutor;
taskExecutor.execute(new Thread(.....));
五、考试
现有一个线程池,参数corePoolSize = 5,maximumPoolSize = 10,BlockingQueue阻塞队列长度为5,此时有4个任务同时进来,问:线程池会创建几条线程?
如果4个任务还没处理完,这时又同时进来2个任务,问:线程池又会创建几条线程还是不会创建?
如果前面6个任务还是没有处理完,这时又同时进来5个任务,问:线程池又会创建几条线程还是不会创建?
回答:
线程池corePoolSize=5,线程初始化时不会自动创建线程,所以当有4个任务同时进来时,执行execute方法会新建【4】条线程来执行任务;
前面的4个任务都没完成,现在又进来2个队列,会新建【1】条线程来执行任务,这时poolSize=corePoolSize,还剩下1个任务,线程池会将剩下这个任务塞进阻塞队列中,等待空闲线程执行;
如果前面6个任务还是没有处理完,这时又同时进来了5个任务,此时还没有空闲线程来执行新来的任务,所以线程池继续将这5个任务塞进阻塞队列,但发现阻塞队列已经满了,核心线程也用完了,还剩下1个任务不知道如何是好,于是线程池只能创建【1】条“临时”线程来执行这个任务了;
这里创建的线程用“临时”来描述还是因为它们不会长期存在于线程池,它们的存活时间为keepAliveTime,此后线程池会维持最少corePoolSize数量的线程。
六、写到最后
作者:极致研发部--申冠豪