线程池-ThreadPoolExecutor
使用场景
想要频繁的创建和销毁线程的时候
线程池的概念
线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的
线程池的优势
- 降低创建线程和销毁线程的性能开销
- 提高响应速度,当有新任务需要执行是不需要等待线程创建就可以立马执行
- 合理的设置线程池大小(限流)可以避免因为线程数超过硬件资源瓶颈带来的问题
Api Executors
-
newFixedThreadPool
该方法返回一个固定数量的线程池,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行,用途:FixedThreadPool 用于负载比较大的服务器,为了资源的合理利用,需要限制当前线程数量。
ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); -
newSingleThreadExecutor
创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中。ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
-
newCachedThreadPool
根据实际情况调整线程个数,不限制最大线程数,若用空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在 60 秒后自动回收。ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue());
-
newScheduledThreadPool
创建一个可以指定线程的数量的线程池,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器。ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory);
-
…
线程池参数
线程池中线程总数、运行线程数、空闲线程数、任务队列等之间的关系
- 当【运行的线程数 < corePoolSize】,则直接创建新线程来处理任务,即使线程池中的其他线程是空闲的
- 当【corePoolSize <= 线程池中线程数 < maximumPoolSize】,则只有当workQueue满时,才创建新线程处理
- 当【corePoolSize = maximumPoolSize】,在workQueue没满时,那么请求会放入workQueue,等待空闲线程去除任务处理
- 当【运行的线程数 > maximumPoolSize】,如果workQueue已满,那么会根据指定策略来处理提交过来的任务
ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //超出核心线程数量以外的线程空余存活时间
TimeUnit unit, //存活时间单位
BlockingQueue<Runnable> workQueue, //保存执行任务的队列
ThreadFactory threadFactory, //创建新线程使用的工厂
RejectedExecutionHandler handler) //当任务无法执行的时候的处理方式
任务提交
- execute();//任务提交
- submit(); //带有返回值的任务提交
- …
最佳线程数
最佳线程数目 = (线程等待时间+任务执行时间)/任务执行时间 * CPU数目
备注:这个公式也是前辈们分享的,当然之前看了淘宝前台系统优化实践的文章,和上面的公式很类似,不过在CPU数目那边,他们更细化了,上面的公式只是参考。不过不管什么公式,最终还是在生产环境中运行后,再优化调整。
例如服务器CPU核数为4核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:( 80 + 20 )/20 * 4 = 20。也就是设置20个线程数最佳。