线程池的使用(实际中的应用)
我们前边说过3中比较重要的线程池的获得方式
但是在实际的工作中获得线程池并不使用这三种方式,阿里巴巴开发手册中有关线程资源的获取和线程池的使用规定如下:
意思即这几种自带的创建线程池的方法中:
- FixedThreadPool和SingleThreadPool中定义的阻塞队列为LinkedBlockingQueue类型,它的默认阻塞边界值为Integer.MAX_VALUE,太大了,会导致阻塞队列中堆积大量请求
- 而CachedThreadPool默认允许创建的maximumPoolSize(最大线程数)为Integer.MAX_VALUE,太大了,会导致创建大量的线程
实际开发中使用自定义的线程池:
代码示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 自定义线程池:
*/
public class MyThreadPoolDemo {
public static void main(String[] args){
//自定义线程池,此处使用的是定义了7个参数
ExecutorService threadPool = new ThreadPoolExecutor( 2,
5,
1L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try{
//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
//此处设置的拒绝策略为AbortPolicy(),当处理线程都还没处理完第一轮所有请求的情况下,10个请求线程超过了最大线程数加阻塞队列最大值数
//无法处理多出的请求,就会启动拒绝策略,这里会抛出异常
for(int i=1;i<=10;i++){
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 办理业务");
});
}
}catch(Exception e){
e.printStackTrace();
}finally{
threadPool.shutdown();//关闭池子
}
}
}
运行结果:
-
这种如果前边请求的线程被处理较快,10个线程都可以得到处理,也不会抛出异常
-
采用其他的拒绝策略,结果也不一样
那么如何合理配置线程池?主要是最大线程数如何合理配置?
答案: 分为两种情况
- 1)CPU密集型
- 2)IO密集型
对于第1种情况
首先运行:看当前系统是几核几线程
CPU密集型任务配置尽快能少的线程数量
//Runtime.getRuntime().availableProcessors()获得CPU为几线程
System.out.println(Runtime.getRuntime().availableProcessors());
一般公式:cpu核心数+1
补充说明:
- CPU密集型任务是指该任务需要大量运算,而没有阻塞,CPU一直全速运行
- CPU密集型任务只有在真正的多核CPU上才有可能得到加速(通过多线程)
对于第2种情况
一般公式:1):cpu核心数*2
一般公式:2):cpu核心数 / ( 1 - 阻塞系数 )
阻塞系数在0.8~0.9之间,一般取个乐观值0.9
补充说明:
- IO密集型任务是指线程并不是一直在执行任务的,因为多次IO,大部分线程都阻塞,应该配置尽可能多的线程
- IO密集型任务即该任务需要大量的IO,即大量的阻塞,在单线程上运行IO密集型任务会导致浪费大量的CPU运算能力浪费在等待上
- 所以在IO密集型任务中使用多线程可以大大加速程序运行,即使在单核CPU上,这种加速主要就是利用被浪费掉的阻塞时间