一、线程池的概念
1、为什么使用线程池
程序的运行本质是占用系统资源,系统资源是有限的,所以我们要优化资源的使用!就 出现了池化技术
2、什么是线程池
可以理解为事先准备好一些资源,要有人用就来池里面拿着。用完后在放到池中。
二、线程池的好处
1、降低资源的消耗
2、提高响应的速度
3、方便管理
总:线程复用,可以控制最大并发数,管理线程
三、线程池的创建
线程的创建有三种方法,使用Executors类中有三个实现方法
newSingleThreadExecutor() | 单线程 |
newFixedThreadPool() | 固定线程池 |
newCachedThreadPool() | 可伸缩的线程池 |
代码:
package com.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//线程池
public class pool {
public static void main(String[] args) {
ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//单个线程
ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//固定线程池大小
ExecutorService threadPool3 = Executors.newCachedThreadPool();//可伸缩的线程池
try {
for (int i = 0; i < 5; i++) {
final int sum = i;
threadPool1.execute(() -> {
System.out.println(Thread.currentThread().getName()+"-------"+sum);
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool1.shutdown();
}
}
}
四、七大参数
举例理解七大参数
现在有一家火锅店里面有9个座位(看成是最大核心线程池大小),外面有3把等候的椅子(队列)。平常火锅店里的人不多,所以就只开放5个座位(看成是核心线程池大小),今天是周六来火锅店吃饭的人特别多,里面的5个座位都坐满了(等于核心线程池大小),外面等候区的3个椅子也满了(队列)。这时候又来了一个人,座位6就会被开发使用,又加一个人的时候,座位7也可以使用,一直到座位9个都开放(达到了最大核心线程池大小),外面等候区3个人也都满了。这时候又来了一位顾客,他等待一会(超时等待)如果前面还是没位置,那么他就到别的地方去了(释放掉)。
1、线程池创建的本质是:ThreadPoolExecutor类
点击进入源码就能知道
源码的代码如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
2、在进入new ThreadPoolEXecutor类中
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
3、可以看到创建线程池的七大参数
七大参数 | 概述 |
int corePoolSize | 核心线程池大小 |
int maximumPoolSize | 最大线程池大小 |
long keepAliveTime | 超时没调用就释放 |
TimeUnit unit | 超时单位 |
BlockingQueue<Runnable> workQueue | 阻塞队列 |
Executors.defaultThreadFactory() | 线程工厂(一般不用改变) |
defaultHandler | 拒绝策略 |
4、在我们日常工作中,线程池是不允许使用Executors创建的,而是通过ThreadPoolExecutor的方式创建,规避资源耗尽的风险;
在Executors创建线程池,允许创建的线程池为Integer.Max_Value,约为21亿
可能创建大量线程,从而导致OOM
五、自定义线程池
1、一般推荐使用自定义线程池
代码:
package com.pool;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//自定义线程池
public class DIYPool {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2, //核心线程池大小
5,//最大核心线程池大小
2, //超时时间
TimeUnit.SECONDS, //超时单位
new LinkedBlockingDeque<>(3), //阻塞队列
Executors.defaultThreadFactory(), //线程工厂
new ThreadPoolExecutor.DiscardPolicy() //拒绝策略
);
try {
for(int i=1;i<=10;i++){
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
}
2、拒绝策略分为4种
new ThreadPoolExecutor
.AbortPolicy() | 队列满了,丢掉任务,抛出异常 |
.CallerRunsPolicy() | 哪里来到哪里去 |
.DiscardPolicy() | 队列满了,丢掉任务,不抛异常 |
.DiscardoldestPolicy() | 队列满了,尝试去和最早的竞争,无异常 |
六、最大核心线程池大小该如何定义(扩展)
1、cpu密集型
查看电脑cpu是几何最大线程就是几,可以保持cpu的效率最高
获取cpu的核数:
Runtime.getRuntime().availableProcessors()
2、Io密集型
判断程序中十分耗Io的线程,一般大于Io线程的2倍就好