池化技术
池化技术在java中有广泛的运用,比如数据库连接池,字符串池,以及线程池。之所以池化技术运用广泛,在于他的优势(以线程池为例):
- 提高线程的利用率
- 提高响应速度
- 便于统一管理线程对象
- 可控制最大并发数
线程池的设计思想
- 核心池大小
- 线程池最大容量
- 等待队列
- 拒绝策略
业务流程如下:
线程池启动的时候会按照核⼼池的数来创建初始化的线程对象 2 个。
开始分配任务,如果同时来了多个任务, 2 个线程对象都被占⽤了,第 3 个以及之后的任务进⼊等待队列,当前有线程完成任务恢复空闲状态的时候,等待队列中的任务获取线程对象。
如果等待队列也占满了,⼜有新的任务进来,需要去协调,让线程池再创建新的线程对象,但是线程池不可能⽆限去创建线程对象,⼀定会有⼀个最⼤上限,就是线程池的最⼤容量。
如果线程池已经达到了最⼤上限,并且等待队列也占满了,此时如果有新的任务进来,只能选择拒绝,并且需要根据拒绝策略来选择对应的⽅案。
线程池代码理解
1.Executors工具类的使用:
- ExecutorService e= Executors.newSingleThreadExecutor();(单线程)
- ExecutorService executorService =Executors.newFixedThreadPool(5);(固定数量现线程池)
- ExecutorService executorService = Executors.newCachedThreadPool();(缓存,线程数油电脑配置定)
看上述三个Executors工具类源码可发现他们有一个共同点:都是要new一个ThreadPoolExecutor 对象。ThreadPoolExecutor就是我们要重点理解的。
2.ThreadPoolExecutor
进入此类发现有许多不同参数的构造器,以便使用者灵活 创建自己需要的线程池。其中最重要的是理解7个参数:
corePoolSize:核⼼池的⼤⼩
maximumPoolSize:线程池的最⼤容量
keepAliveTime:线程存活时间(在没有任务可执⾏的情况下),必须是线程池中的数量⼤于
corePoolSize,才会⽣效
TimeUnit:存活时间单位
BlockingQueue:等待队列,存储等待执⾏的任务
ThreadFactory:线程⼯⼚,⽤来创建线程对象
RejectedExecutionHandler:拒绝策略
四种拒绝策略:
1、AbortPolicy:直接抛出异常
2、DiscardPolicy:放弃任务,不抛出异常
3、DiscardOldestPolicy:尝试与等待队列中最前⾯的任务去争夺,不抛出异常
4、CallerRunsPolicy:谁调⽤谁处理
代码实验
已知以上概念,我们可以很轻易地写出自己想要的线程池。
主要代码:
package com.blog.www.common;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;
/**
* @version 1.0
* @date 2020/3/8 21:37
*/
public class test {
public static void main(String[] args) {
//定制化线程池
ExecutorService executorService = new ThreadPoolExecutor(2,3,0L,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for(int i=0;i<1;i++){
executorService.execute(()->{
try {
TimeUnit.MILLISECONDS.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"===>办理业务");
});
}
}
}
1.i<1时,单线程结果很明显:
2、i<2时候,此时正好达到核心线程数(结果不唯一)
3、i<3时,for是并发的,第三个任务来会进入等待队列,不会新开线程。
4、i<4时,和上面相同,只不过等待队列刚好满了
5、i<5时,第五个任务来了,此时两个核心线程在运行,等待队列也满了,所以线程池会再创建一个线程,此时就达到我们设置的最大线程上限了。
6.i<6时,第六个任务来了,此时等待队列满了,也已达到最大线程上限。故只有拒绝此任务。
7.i<7时,第七个任务来了。因为拒绝了第六个任务,同时也有其他任务结束的,故第七个任务也有可能正常执行,当然也有可能被拒绝。这里要改一下主代码,因为决绝策略用的是AbortPolicy直接抛出异常,这样看不出来效果。换成CallerRunsPolicy:谁调⽤谁处理。
8.i<8时,多一点就可以看出来,后面的任务还有可能被执行的。
至此,定制线程池成功!