第一个问题:线程是不是越多越好?
- 线程本身就是一个java对象,需要销毁系统资源,线程创建和销毁都需要时间。
如果执行任务的时间跟创建、销毁线程的时间差不多,就没必要使用线程。 - java对象占用堆内存,操作系统线程占用系统内存,根据jvm规范,一个线程默认最大栈空间是1m,这个栈空间是需要从系统内存中分配的,线程过多会消耗很多内存。
- 操作系统需要频繁切换线程上下文(线程都想被执行),会影响性能。
线程池的产生就是为了方便控制线程数量,节约系统开销。
相关概念:
- 线程池:用于管理线程,创建、销毁线程。
- 工作线程:线程池管理的线程,可以被复用,没有任务的时候处于等待状态。
- 任务接口:每个任务必须实现的接口,主要规定了任务入口,任务状态,执行完的收尾工作。在java中表现为Runnable接口。
- 任务队列:一种缓冲机制,用于存放没有执行的任务。
线程池执行任务的流程:
image.png
- 先保证核心线程的数目,不够则立刻创建新线程执行。
- 再看工作队列,没满则直接把任务丢到工作队列。
- 如果工作队列也满了,不得不执行了,就要看当前线程数目是否达到最大线程数,没达到就创建新线程执行,否则就拒绝执行。
(这种资源调度的理念让我想到工作场景,程序员工作不饱和就立马拉来干活,任务过多就先排着留给程序员加班处理,加班还干不完就根据预算招外包,压力期过了外包都闲了就可以辞掉,需求多到干不完就拍桌子推掉~)
线程池的最大任务容量:最大线程数+等待队列数,如果超过了就会拒绝执行,任务拒绝执行默认会抛出异常,可以自己写handler处理。
java中实现线程池的接口和实现类
Executor: 最上层接口,规定了执行任务的方法execute。
ExecutorService: 继承了Executor接口,扩展了callable、Future、关闭方法。
ScheduledExecutorService: 继承了ExecutorService接口,增加了定时任务相关方法。
ThreadPoolExecutor:标准实现类,我们通常都使用这个实现类。
ScheduledThreadPoolExecutor:继承了ThreadPoolExecutor,同时实现了ScheduledExecutorService中定时任务相关方法。
代码实例:
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
线程设置多少合适?
- 计算密集型的任务:比较消耗cpu资源,参考值为cpu内核的2倍;
- io密集型:例如请求网络接口,文件读取等,根据io时长考量设置,参考值tomcat默认最大线程数200,或者考虑自己定义最小值和最大值,让线程动态增减。
检查cpu利用率是否合理
通常理想情况cpu的利用率在80%上下为理想值
关于同步队列可以参考
https://ifeve.com/java-synchronousqueue/
另附某大神的博客,里面很多jvm相关知识:https://blog.csdn.net/yfqnihao/article/details/8289363












网友评论