线程池
池化技术
由于程序的运行,本质是在占用系统的资源!而为了优化资源的使用,就诞生了池化技术,比如有线程池、常量池、JDBC的连接池…
线程池的好处:
- 1、降低资源的消耗
- 2、提高响应的速度
- 3、方便管理
4、线程复用,可以控制最大并发数,管理线程
线程池:三大方法,七大参数,四种拒绝策略
三大方法
一、创建单个线程池 => Executors.newSingleThreadExecutor();
ExecutorService threadExecutor = Executors.newSingleThreadExecutor();// 单个线程
// 启动线程池
try {
for (int i = 0; i < 10; i++) {
threadExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭线程池
threadExecutor.shutdown();
}
结果:所有线程都在1个线程池里
二、创建一个固定大小的线程池 => Executors.newFixedThreadPool(5);
ExecutorService threadPool = Executors.newFixedThreadPool(5);// 创建一个固定大小的线程池
// 启动线程池
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();
}
结果:固定大小,最多5个线程池
三、创建可伸缩的线程池 => Executors.newCachedThreadPool();
ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的线程池
// 启动线程池
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();
}
结果:可以根据有多少个线程来建多少个线程池(最多能多少个也不确定,反正遇强则强)!
创建线程池的正确打开方式
然而,阿里巴巴开发手册上却不允许使用 Executors
类来创建线程池,要使用原生的 ThreadPollExecutor
来创建线程池:
所以正确创建建方式为:
为方便理解,就以一个银行取钱的实例来模仿创建线程池
// 自定义线程池(在工作中只会使用这种方式)
ExecutorService threadPool = new ThreadPoolExecutor(
2, // 银行默认开启2个窗口服务
5, // 银行总共有五个窗口
3, // 休息区满后又来人,银行就开启另外三个窗口,如果这三个窗口在三秒内没人来,就会再次关闭
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3), // 休息区
Executors.defaultThreadFactory(), // 创建线程
new ThreadPoolExecutor.AbortPolicy() // 银行所有东西都满了,却还有人进来,就不处理这个人了,并抛出异常
);
// 启动线程池
try {
for (int i = 1; i <= 9; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭线程池
threadPool.shutdown();
}
结果:当i超过最大承载量(窗口数+休息区数)时,就会报错。
七大参数
七大参数就创建线程池所需的7个参数:
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
拒绝策略
拒绝策略就是创建线程池的最后一个参数,一共如上图所示,有4种策略:
ThreadPoolExecutor.AbortPolicy()
:队列就会报错;
ThreadPoolExecutor.CallerRunsPolicy()
:哪来的就哪去,就是超出后来的由 main 线程去处理;
ThreadPoolExecutor.DiscardPolicy()
:队列满了就抛弃超出的任务,但不会抛出异常;
ThreadPoolExecutor.DiscardOldestPolicy()
:队列满后,尝试去和最早的线程竞争,也不会抛出异常;
问题:线程池的最大容量该如何定义(面试会问)?
两种解决方案(这是用来调优的了):
一、CPU 密集型
即有几核 CPU,就会有几个线程可以同时执行。所以你是几核 CPU,就定义为几,可以保证 CPU 效率最高。
通过代码获取电脑 CPU 的核数:
Runtime.getRuntime().availableProcessors();
二、IO 密集型
就是判断你的程序中,非常耗 IO 的线程有几个,那么定义的数大于几就可以了(一般可以设置为大于几的两倍)。