1、什么是池化技术?
池化技术 (Pool) 是一种很常见的编程技巧,在请求量大时能明显优化应用性能,降低系统频繁建连的资源开销。我们日常工作中常见的有数据库连接池、线程池、对象池等,它们的特点都是将 “昂贵的”、“费时的” 的资源维护在一个特定的 “池子” 中,规定其最小连接数、最大连接数、阻塞队列等配置,方便进行统一管理和复用,通常还会附带一些探活机制、强制回收、监控一类的配套功能。
简介
**提前保存大量的资源,以备不时之需以及重复使用。**池化技术应用广泛,如内存池,线程池,连接池等等。内存池相关的内容,建议看看 Apache、Nginx 等开源 web 服务器的内存池实现。
**由于在实际应用当做,分配内存、创建进程、线程都会设计到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作。**因此,当程序中需要频繁的进行内存申请释放,进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池技术来提升程序的性能。
对连接或线程的复用,并对复用的数量、时间等进行控制,从而使得系统的性能和资源消耗达到最优状态。
池化技术简单点来说,就是提前保存大量的资源,以备不时之需。
2、JAVA中的线程池
2.1使用Executors 创建(不推荐)
代码实现:
public class ThreadPoolTest {
public static void main(String[] args) {
//创建只含有一个线程的线程池
//ExecutorService threadPool = Executors.newSingleThreadExecutor();
//创建含有多个线程(需要多少就在参数处填多少)的线程池
//ExecutorService threadPool = Executors.newFixedThreadPool(5);
//创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
ExecutorService threadPool = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 100; i++) {
//使用线程池创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
threadPool.shutdown();
}
}
}
使用newSingleThreadExecutor()
:
可以看到从头到尾只有一个线程在执行
使用newFixedThreadPool(5)
:
可以看到从头到尾有5个线程在执行
使用newCachedThreadPool()
:
2.2使用ThreadPoolExecutor创建(推荐)
当我们进去newSingleThreadExecutor()
,newFixedThreadPool()
,newCachedThreadPool()
的源码就会看到
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
原来都是使用ThreadPoolExecutor
进行创建的
为什么推荐使用ThreadPoolExecutor创建线程池?
阿里巴巴-JAVA开发手册给我们解释了
注:OOM:全称“Out Of Memory”,翻译成中文就是“内存用完了”,当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(注:非exception,因为这个问题已经严重到不足以被应用处理)。
在使用ThreadPoolExecutor创建线程池之前我们先要了解ThreadPoolExecutor的7个参数
int corePoolSize
// 核心线程池大小int maximumPoolSize
// 最大核心线程池大小long keepAliveTime
// 超时了没有人调用就会释放TimeUnit unit
// 超时单位BlockingQueue workQueue
// 阻塞队列ThreadFactory threadFactory
// 线程工厂:创建线程的,一般 不用动RejectedExecutionHandler handle
// 拒绝策略
举个例子说明这7个参数的意思
场景:银行办理业务,共有10窗口,初始开了5个窗口,要是人流量多5个窗口满足不了需要,就需要让关闭的5个窗口开启一些直到满足需求,要是有窗口超过一定的时间没人使用那么这个窗口就会关闭,要是10个全部开启都满足不了,就需要在候客区等待(候客区也有容量),要是连候客区也满了这时候银行就需要拒绝后面的人进入银行。
在上面的场景中:
初始开启的窗口就是int corePoolSize
(当初始的线程池大小满足不了需求,就会扩大线程池的大小,上限是int maximumPoolSize
)
共有10窗口就是int maximumPoolSize
(最大核心线程池大小,最多的线程数)
窗口超时关闭就是long keepAliveTime
(超时了没有人调用就会释放 )
TimeUnit unit
( 超时单位:天,时,分,秒。。。)
候客区就是BlockingQueue workQueue
(线程池中的线程都被请求走了,后面要请求线程就需要进入阻塞队列等待)
ThreadFactory threadFactory
(一般不用动)
拒绝后面的人进入银行RejectedExecutionHandler handle
(在阻塞队列也满了后,就会执行的拒绝策略)
代码:
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,//初始线程数为2
5,//最大线程数为5
3,//超时3回收
TimeUnit.SECONDS,//秒
new LinkedBlockingQueue<>(3),//阻塞队列容量为3
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy()//使用DiscardPolicy拒绝策略
);
try {
for (int i = 0; i < 100; i++) {
//使用线程池创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
threadPool.shutdown();
}
}
}
四种拒绝策略的说明
new ThreadPoolExecutor.AbortPolicy()
// 队列满了,还有人进来,不处理这个人的,抛出异 常new ThreadPoolExecutor.CallerRunsPolicy()
// 哪来的去哪里! (主线程来的,回去让主线程做)new ThreadPoolExecutor.DiscardPolicy()
//队列满了,丢掉任务,不会抛出异常!new ThreadPoolExecutor.DiscardOldestPolicy()
//队列满了,尝试去和最早的竞争,也不会 抛出异常!
拓展:
IO 密集型:系统运作,大部分的状况是CPU 在等I/O (硬盘/内存)的读/写。
CPU 密集型:大部份时间用来做计算、逻辑判断等CPU 动作的程序称之CPU 密集型。
在CPU 密集型的情况下直接将线程池的线程数设为CPU的核数即可
Runtime.getRuntime().availableProcessors()
可有获取CPU的核数
参考:
http://t.csdn.cn/APCaR