背景
为什么出现线程池?
- 目前计算机CPU均为多核处理器,如果串行执行,必然造成资源上的浪费;
- 每次创建线程的时候,和销毁线程的时候,都不是我们程序关心的过程,程序关心的只是执行
- 有些业务程序不一定要串行执行,实际上可以并行去执行的;以节约相应时间
- 多个线程执行业务,那么怎么管理这些线程呢,是否可以统一管理
使用线程池的好处
- 充分利用CPU资源
- 提高相应时间
- 统一管理线程(比如线程优先级、监控等)
线程池工作原理
线程池,字面上理解肯定有一个池子,这个池子可以是集合或者队列等,java中使用的是队列;池子存在了,那么就需要工作者,就是真实的工作线程;真实的工作线程控制多少呢,真实的工作者的个数;这些都存在了,那么往池子中注入业务是否需要一个入口,java中就是submit
现实中例子:线程池好比一间客服办公室;办公室为池子;办公室必须保证n个接线员以上,n为核心线程数,工位数为最大线程数;接线员为真实的工作线程;电话号码(如10086)位线程池作业提交入口;超出的n个电话业务而等待的电话咨询数,为等待队列;如果等待的人数超出能等待的范围,就添加接线员到新的工位上,接电话;
线程池UML
线程池采用模板模式
线程池UML.pngThreadPoolExecutor原理图
线程池原理.png- 运行线程数小于 corePoolSize,则创建新的线程来执行(创建新的线程,需要获取全局锁,这时候,其它线程可能在创建或者销毁等)
- 如果运行线程数大于等于corePoolSize,则任何加入BlockingQueue
- 如果BlockingQueue已满,maximumPoolSize>corePoolSize,则创建新的线程来处理任务(创建线程需要获取全局锁)
- 如果队列满,且maximumPoolSize也满,则执行拒绝策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize : 当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其它空闲的基本线程能够执行任务也会创建线程,一直到任务数>=核心线程数时就不再创建。有prestartAllCoreThread()方法,可以提前创建
- workQueue:任务队列
- ArrayBlockingQueue:基于数组实现
- LinkedBlockingQueue:基于链表实现,吞吐量高于前者
- SynchronousQueue:一个不存储元素的阻塞队列;没插入操作必须等另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量又高于前者
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列
- maximumPoolSize:线程池最大数量;如果队列满了,并且已创建的线程数小于最大线程数,则线程池会创建新的 线程执行任务;如果使用了无界队列的话,这个参数就没有效果
- threadFactory:设置创建线程的工厂(可以设置创建的线程名称)
- RejectedExecutionHandler:拒绝策略;当线程池都满了,说明线程池处于饱和状态,那么必须采用一种策略处理新提交的任务。
- AbortPolicy:直接抛出异常(默认)
- CallerRunsPolicy:只用调用者所在的线程来运行任务
- DiscardOldestPolicy:丢弃队列中最近的一个任务,并执行当前任务
- DiscardPolicy:不处理,直接丢弃掉
- keepAliveTime:线程池的工作线程空闲后,保存时间
- TimeUnit:保存时间单位
线程池提交任务
两种方式:
- execute() :提交不需要返回值的任务
- submit():提交需要返回值的任务,返回类型为Future<Object>
关闭线程池
shutdown,shutdownNow两种方式。原理都是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,无法响应中断的任务可能永远无法终止。
网友评论