线程的创建消耗大量资源和时间,在利用线程池,则会大大的提高了资源的利用率,提高性能。
线程池是一种池化技术,通过提前创建一批线程并缓存起来,当有一个任务来执行时,从线程池中选择一个空闲线程来执行任务。
池化技术能够减少资源对象的创建次数,提高程序的性能,特别是在高并发下这种提高更加明显。简单点来说,就是提前保存大量的资源,以备不时之需。
池化技术创建的资源的特点:对象创建时间长、对象创建需要大量资源、对象创建后可被重复使用
扩展知识
Executors
Executors 的创建线程池的方法,创建出来的线程池都实现了ExecutorService的接口
- newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程
- newCachedThreadPool: 创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,缓存型池子通常用于执行一些生存期很短的异步型任务
- newScheduledThreadPool: 创建一个定长线程池,支持定时及周期性任务执行
- scheduleAtFixedRate: 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推
- scheduleWithFixedDelay: 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟
- newSingleThreadExecutor: 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
- 自定义线程池
ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), new ThreadPoolExecutor.AbortPolicy());
}
线程池核心原理
通常,一般构造函数会反映这个工具的基本的数据存储结构
- corePoolSize: 核心线程数,启动线程后,驻留的线程
- maximumPoolSize: 最大线程数,核心线程数都在执行时,会开启生成临时线程,maximumPoolSize = corePoolSize + 临时线程数
- workQueue:任务等待队列,当执行任务的线程数超过maximumPoolSize时,会将任务放入到队列中,等待被执行
- keepAliveTime: 临时线程空闲多长时间被释放掉
- threadFactory: 创建线程的工厂,用于设置线程的自己的属性
- handler: 拒绝策略,当任务都超过了等待队列的执行时,会抛出拒绝异常
添加一个任务
使用线程池调用execute() 执行任务的方法,其实就是将任务添加到线程池中。
- 第一步:判断工作线程 小于核心线程数 则添加一个线程任务。成功则直接返回,没有则会进入第二步
- 第二步:线程池非关闭,添加任务到任务队列中,再次判断线程池状态,因为此时需要添加一个非核心线程任务去启动了。
- 第三步:添加一个非核心线程任务处理,不成功是则拒绝该任务
:::tips
为什么会反复的检查线程池的状态,因为在任务之后,可能当前线程池shutDown或者stop了,那么这个时候,就需要拒绝该任务。要意识到这一点,在多线程中,某些状态值会经常的发生变化,需要时常去关注。
:::
:::tips
这里有一个问题,如果核心线程数为0时,线程池会怎样?任务依旧会被执行,此时任务会先添加到队列,然后去判断是否可以启动非核心线程去执行任务。
:::
添加Worker线程
addWorker(Runnable firstTask, boolean core) 主要是添加线程
- 判断线程池是否为启动,非启动状态,直接返回
- 自旋的方式,添加线程数,如果线程数超过了最大线程或核心线程,则直接返回
- 创建Worker线程对象
- 获取线程池全局锁
- 添加worker线程到workers队列中(线程池)
- 添加成功则执行线程
Worker线程执行任务
- 先执行firstTask任务,firstTask任务为空时,则冲worksQueue队列中阻塞时获取任务
- 线程池非启动状态,或线程非中断
- 执行任务之前的钩子
- 执行任务
- 任务之后之后的钩子
总结
- 调用execute(),如果还有核心线程数,则直接添加Worker,否则会将任务添加到队列中
- 调用addWorker(), new 一个Worker对象,并调用worker.run方法执行任务
- worker线程任务不断去拉取任务执行
处理流程: