池化技术
程序运行的本质:程序一旦运行,就会占用系统资源!优化资源的使用!
什么是池化技术
池化技术 (Pool) 是一种很常见的编程技巧,在请求量大时能明显优化应用性能,降低系统频繁建连的资源开销。我们日常工作中常见的有数据库连接池、线程池、对象池等,它们的特点都是将 “昂贵的”、“费时的” 的资源维护在一个特定的 “池子” 中,规定其最小连接数、最大连接数、阻塞队列等配置,方便进行统一管理和复用,通常还会附带一些探活机制、强制回收、监控一类的配套功能。
线程池的好处
- 降低资源的消耗
- 提高响应的速度
- 方便管理
线程可以复用,可以控制最大并发数,可以管理线程
线程池:三大方法、7大参数、4种拒绝策略
三大方法(Executors)
ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线
程
// ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一
个固定的线程池的大小
// ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩
的,遇强则强,遇弱则弱
需要注意两点
- 在使用线程池之后,我们应该使用线程池来创建线程,而不是之前的new Thread去创建
- 线程池用完,程序结束时,关闭线程池
try {
for (int i = 0; i <10 ; i++) {
//使用线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束时,关闭线程池
threadPool.shutdown();
}
}
7大参数
首先分析一下上面三个方法的底层
单个线程
创建一个固定的线程池的大小
可伸缩的,遇强则强,遇弱则弱
通过上面上个底层代码我们可以知道本质上调用的都是:ThreadPoolExecutor
通过进入他的底层,在通过this可以看到下面的底层代码!在这里面我们就可以看到7大参数!
7大参数的意义如下:
int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 线程池最大容量
long keepAliveTime, // 超时了没有人调用就会释放
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂:创建线程的,一般不用动
RejectedExecutionHandler handle // 拒绝策略)
四种拒绝策略(线程池饱和策略)
- (默认的)new ThreadPoolExecutor.AbortPolicy() // 队列满了,还有人进来,不处理这个人的,抛出异常
- new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!
- new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
- new ThreadPoolExecutor.DiscardOldestPolicy() //当任务被拒绝添加时,会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去。在rejectedExecution先从任务队列中弹出最先加入的任务,空出一个位置,然后再次执行execute方法把任务加入队列。即将最早进入队列的任务删除,之后再尝试加入队列
手动创建一个线程池
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,//核心数
5,//最大线程数
3,//超时了没有人调用就会释放
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),// 阻塞队列
Executors.defaultThreadFactory(),// 线程工厂:创建线程的,一般不用动
new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略
try {
//最大承载:队列容量+最大线程数
for (int i = 0; i <9; i++) {
//使用线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束时,关闭线程池
threadPool.shutdown();
}
}
注意点:
最大承载:队列容量+最大线程数
密集型(调优)
之前的学习中,我们可以创建线程池了,但是如何确定线程池的大小呢?
- CPU 密集型 :cpu使用率较高(也就是一些复杂运算,逻辑处理),所以线程数一般只需要cpu核数的线程就可以了。 这一类型的在开发中多出现的一些业务复杂计算和逻辑处理过程中。
- IO 密集型:cpu使用率较低,程序中会存在大量I/O操作占据时间,导致线程空余时间出来,所以通常就需要开cpu核数的两倍的线程, 当线程进行I/O操作cpu空暇时启用其他线程继续使用cpu,提高cpu使用率 通过上述可以总结出:线程的最佳数量: 最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目 线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。