1.什么是线程池
就是可管理和维护以及分配线程的 容器
2.使用线程池的优点
优化线程的内存开销
3.ThreadPoolExecutor的核心参数
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程空闲时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//拒绝策略)
4.线程池的执行顺序
- 当线程数小于核心线程数,且存在未执行的任务,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满,若线程数小于最大线程数,创建线程。
- 若线程数等于最大线程数,则执行拒绝策略
5.线程池的参数详解
①corePoolSize:核心线程数(一般与线程具体参与的业务有关-计算类型/读写类型)
计算类型-CPU密集型 ;读写类型-IO密集型
②maximumPoolSize:最大线程数-考虑项目业务最大程度上最大容量的线程数
③keepAliveTime:最大空闲时间,非核心线程超过最大空闲时间则释放
④unit:时间单位-秒-分钟-时-天
⑤workQueue:当核心线程都处于工作,任务进行队列中等待
⑥threadFactory:线程工厂
⑥handler:拒绝策略-抛异常-不抛异常-自定义策略
当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务。默认会抛出异常
6线程池的工作机制
前提:在如下场景中,核心线程数为2,最大线程数为4,任务队列为2
刚开始,没有任何的线程和任务
此时,来了一个任务一, 线程池(公司)生产一个线程(小王)处理任务一
此时,又来了任务二, 线程池(公司)生产一个线程(小李)处理任务二
此时,陆续来了任务三,任务四,由于当前线程数(2)等于核心线程数(2),且任务队列未满时,将任务陆续放入任务队列。
此时,来了任务5,任务队伍已满
任务队列已满,且核心线程数已满,线程池(公司)招临时工小怡处理任务五
又来了任务六,此时线程数3(2+1),未超过最大线程数4,可以再招一个临时工小皮处理任务六
此时又来了一个任务七 ,最大线程数已达极限且任务队列已满,调用拒绝策略处理任务七
---以上是我的初步了解线程池机制的动画图,如有帮助可以点赞支持一下!!!感谢观看
7.自定义线程池
项目在启动的时候,@Bean方法返回的ThreadPoolExecutor 对象也会立马存入容器当中!
@Configuration
@Data
public class ThreadPoolExecutorConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(){
//线程工厂
ThreadFactory threadFactory = new ThreadFactory() {
private int count = 1;
@Override
public Thread newThread(@NotNull Runnable r) {
Thread thread = new Thread(r);
thread.setName("线程"+ count);
count++;
return thread;
}
};
//新建ThreadPoolExecutor对象并配置相关参数
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
4,
100,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(4),
threadFactory
);
return threadPoolExecutor;
}
}
自定义线程处理方法
get():查看线程池的相关信息
add():新增线程
CompletableFuture
提供了静态方法来创建一个异步操作:
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
8此方法以Runnable
函数式接口类型为参数,没有返回结果,使用指定的线程池运行
@RestController
@RequestMapping("/queue")
@Slf4j
public class QueueController {
@Resource
private ThreadPoolExecutor threadPoolExecutor;
@GetMapping("/add")
public void add(String name){
//
CompletableFuture.runAsync(()->{
log.info("任务执行中:"+name+",执行人:"+Thread.currentThread());
try {
Thread.sleep(600000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},threadPoolExecutor);
}
@GetMapping("/get")
public String get(){
HashMap<String, Object> map = new HashMap<>();
int size = threadPoolExecutor.getQueue().size();
map.put("队列长度",size);
long taskCount = threadPoolExecutor.getTaskCount();
map.put("任务总数:",taskCount);
long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
map.put("已完成任务数",completedTaskCount);
int activeCount = threadPoolExecutor.getActiveCount();
map.put("正在工作的线程数",activeCount);
return JSONUtil.toJsonStr(map);
}
}
8.接口文档-测试线程池的工作机制
前提:核心线程数2,最大线程数4,任务队列4
添加任务---临界情况
此时再添加一个任务,系统抛异常-拒绝策略
---面试题
1.什么是线程池?
线程池就是提前创建若干个线程
2.线程池的优点?
重用存在的线程,减少对象创建的开销
有效地控制最大并发数
3.线程池在开发场景中怎么使用?
使用自定义线程池
4.线程池的7大参数
1.corePoolSize,线程池中常驻核心线程数.
2.maximumPoolSize,线程池中可同时执行的最大线程数。
3.keepAliveTime,执行完阻塞队列任务且无新任务,等待临时线程销毁的时间
4.TimeUnit unit,keepAliveTime单位。
5.BlockingQueue workQueue,任务队列,被提交但是未被立马执行的任务。
6.ThreadFactory threadFactory,生成线程的线程工厂。
7.RejectedExecutionHandler handler,拒绝策略。
5.线程池拒绝策略
AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
CallerRunsPolicy 策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前的被丢弃的任务。
DiscardOleddestPolicy策略: 该策略将丢弃最老的一个请求,也就是即将被执行的任务,并尝试再次提交当前任务。
DiscardPolicy策略:该策略默默的丢弃无法处理的任务,不予任何处理。
6.线程池工作机制
7. 核心线程数和最大线程数和工作线程个数的关系?
工作线程数的个数可能从0到最大线程数之间变化,当执行一段时间之后可能维持在核心线程数(corePoolSize),但也不是绝对的,取决于核心线程是否允许被超时回收。
最大线程数 = 核心线程数 + 临时线程数
8.存活时间keepAliveTime的理解?
工作线程数达到核心线程数时,新来的任务将放到有界堵塞队列中,且一段时间队列满了,程序将创建临时工作线程,当阻塞队列的任务执行完毕且没有新任务提交,临时线程将被销毁。这一段等待销毁的时间叫做存活时间keepAliveTime