为什么用线程池
线程池的主要特点有:线程复用、控制最大并发数、线程管理
使用线程池主要目的:
1.降低资源消耗。通过重复利用已经创建的线程降低线程创建和销毁造成的消耗
2.提高响应速度。任务到达时,任务可以不需要等到线程创建就能立即执行。
3.提高线程的可管理性。线程资源如果无限创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可进行统一的分配,调优和监控
结构图
类图关系线程池如何使用
使用线程池工具类创建Executors,常用的线程池有
Executors.newFixedThreadPool(xx)
Executors.newSingleThreadExecutor(xx)
Executors.newCachedThreadPool(xx)
根据需求自定义线程池:
构造方法
线程池的主要参数
参数名 | 类型 | 说明 |
---|---|---|
corePoolSize | int | 线程池中常驻核心线程数 |
maximumPoolSize | int | 线程池能够容纳同时执行的最大线程数,此值必须大于等于1(corePoolSize 和 workQueue满了之后线程池扩容的最大值) |
keepAliveTime | long | 多余的空闲线程存活时间,当前线程池线程数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止 |
unit | TimeUnit | keepAliveTime的单位 |
workQueue | BlockingQueue<Runnable> | 任务队列,被提交但尚未被执行的任务 |
threadFactory | ThreadFactory | 表示生产线程池中工作线程的线程工厂,用于创建线程一般默认即可 |
handler | RejectedExecutionHandler | 拒绝策略,表示队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)之后在提交的线程的拒绝策略 |
工作流程
线程池工作流程线程池工作流程
主线程调用execute()方法添加一个任务时,线程池会有如下逻辑:
1.如果正在运行的线程数量<corePoolSize,马上创建线程运行这个任务
2.如果正在运行的线程数量>=corePoolSize,那么将这个任务放入队列
3.如果队列满了且正在运行的线程数量<maximumPoolSize,创建非核心线程立刻运行这个任务
4.如果队列满了且正在运行的线程数量>=maximumPoolSize,线程池会启动饱和拒绝策略来执行
拒绝策略
策略 | 介绍 |
---|---|
AbortPolicy | 默认策略,触发时直接抛出RejectedExecutionException异常阻止系统正常运行 |
CallerRunsPolicy | “调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将任务返回到调用者运行 |
DiscardPolicy | 直接丢弃任务,不做任何处理,也不抛出异常。如果任务运行丢弃,这是最好的方案 |
DiscardOldestPolicy | 抛弃队列中等待最久的任务,然后把当前任务加入队列尝试再次提交当前任务 |
自定义策略 | 实现RejectedExecutionHandler接口自定义拒绝策略,入将任务放入数据库、提交到redis等,当线程池空闲时读取数据库、redis中的任务执行 |
合理配置线程数
1.corePoolSize可以无所谓最小可以设置为0
2.maximumPoolSize
CPU密集型 核心数+1
IO密集型:任务需要大量的io,即大量的阻塞
1)CPU核心数*2
2) cpu核数 / 1-阻塞系数 阻塞系数0.8~0.9之间 例子:8核cpu 8 / 1-0.9 = 80
这两种可根据实际情况测试对比后设置
网友评论