线程池是不是越多越好?
- 线程在Java中是一个对象,更是操作系统的资源,线程创建、销毁需要时间。如果传教时间+销毁时间>执行任务完成时间就很不合算。
- Java对象占用内存,造作系统线程占用系统内存,根据jvm规范,一个线程默认做大栈的大小是1M,这个栈空间需要从系统内存中分配。线程过多会消耗很多内存。
- 操作系统频繁切换上下文,影响性能。
线程池的推出就是为了方便控制线程数量。
线程池的原理:
- 线程管理器:用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务.
- 工作线程: 线程池中线程在没有任务时处于等待状态,可以循环执行任务;
- 任务接口:每个任务必须实现的接口,以提供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等。
- 任务队列:用于存放没有处理的任务,提供一种缓冲机制。
接口定义和实现类
类型 | 名称 | 描述 |
---|---|---|
接口 | Executor | 最上层的接口,定义了执行任务的方法executor |
接口 | ExecutorService | 继承了Executor接口,拓展了callable、Future、关闭方法 |
接口 | ScheduledExecutorService | 继承了ExecutorService,增加了定时任务的相关方法 |
实现类 | ThreadPoolExecutor | 基础标准的线程池实现 |
实现类 | ScheduledThreadPoolExecutor | 继承了ThreadPoolExecutor实现了ScheduledExecutorService中相关定时任务的方法 |
可以认为ScheduledThreadPoolExecutor是最丰富的实现类
Executors工具类
- newFixedThreadPool(int nThreads)创建一个固定大小任务队列容量无界的线程池,其核心线程数=最大线程数
- newCachedTHreadPool()创建一个大小无界的缓冲线程池,他的任务队列是一个同步队列。任务加入到池中,如果池中有空闲线程则用空闲线程执行,池中线程空闲超过60喵,将被销毁释放,线程数随任务多少变化,适用于执行好事较小的异步任务。池的核心线程数=0,最大线程数=Integer.Max_Value。
- newSingleThreadExcutor()只有一个线程来执行无界队列的单一线程池,该线程池确保任务按加入的顺序一个一个依次执行,当唯一的线程因任务异常中止是,将创建一个新的线程来继续执行后续的任务,与newFixedThreadPool(1)的区别在于单一线程池的池大小在newSingleThreadExcutor()方法中硬编码,不能再改变。
- newScheduledThreadPool(int corePoolSize) 能定时执行任务的线程池。该池的核心线程数有参数指定,最大线程数=Integer.Max_value。
如何确定合适数量的线程池?
- 如果是执行计算型任务线程池的数量为CPU的1-2倍即可。
- 如果是执行IO型任务,相对比计算型任务需要多一些线程,要根据具体的IO阻塞时长运行考量决定,如tomcat中默认最大线程池数量为200。
也可以考虑根据需要在一个最小数量和最大数量将自动增减的线程数。
可以通过监控CPU的情况进行判断线程数量是否合适,如果CPU的运用率达到80%则说明CPU充分利用了,如果CPU小于80%那么整个程序再CPU运用这一块还是不合理,如果太满了可能线程池数量太多CPU处理不过来。