JDK源码走读之深入理解线程池(ThreadPoolExecutor)
来源:互联网 发布:attr 属性 boolean js 编辑:程序博客网 时间:2024/05/21 06:14
Java线程池提供了一个框架来统一管理线程,通过把提交任务和执行任务解耦,使开发者无需关心线程的运行状态,只要把任务提交给线程池既可。使用线程池有以下好处:
1. 减少在创建和销毁线程上所花的时间以及系统资源的开销
2. 避免创建过多的线程导致系统资源过度消耗、系统执行效率低下
线程池可以通过工厂类Executors创建,大致分为以下几种:
1. newFixedThreadPool(int nThreads) 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
2. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
3. newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
4. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
下面深入源码来分析一下线程池的实现
Executor框架
Executor接口是整个框架的基础,下面所有类和接口都是对Executor的扩展。ThreadPoolExecutor和ScheduledThreadPoolExecutor是线程池的俩个核心类,前者是普通的线程池,后者继承前者扩展了任务定时、延迟执行等功能。
Executors则是一个工厂类,屏蔽了线程池的创建过程,通过调用不同的方法传入合适的参数类实例化不同类型的线程池。
## ThreadPoolExecutor 定义##
ThreadPoolExecutor主要由4部分构成
- 线程池的状态,运行、终止、当前线程数等等,用于对线程池监控
- 工作队列,用来保存提交的任务,队列可以有不同类型的实现
- 工作(消费)线程,用来执行工作队列里的任务
- 提交任务方法,以及一些其他方法
任务处理流程
线程池的核心是生产者消费者模型,提交一个新的任务主要流程如图所示:
- 线程池里的线程是否小于核心线程数,如果是则创建一个新的线程执行任务,如果不是执行2
- 工作队列是否已满,如果没有满则把任务放入队列中,如果满了执行3
- 判断线程池是否已满(到达线程个数上限),如果没满则创建新的线程执行任务,如果满了执行4
- 按照定义的不同策略处理无法执行的任务,如直接抛弃任务、抛出异常等等
Worker
Worker为ThreadPoolExecutor的静态内部类,实现了runnable接口并集成了AbstractQueuedSynchonrizer类,这个类有两个主要的成员变量。
- firstTask用来保存runnable任务,提交新任务到线程池,当核心线程池不满时会创建worker并且把提交的任务赋给这个worker,确保当前任务优先执行。
thread对象,每创建一个worker都会对应创建一个thread对象,并且把worker自身作为task传入thread,这样执行thread.start就可以调用到worker的run方法
Worker的run方法调用外部类的runWorker方法- 循环调用getTask()方法获取队列中的任务执行,执行前加锁执行结束释放锁
- 执行任务前后根据业务场景自定义beforeExecute和afterExecute方法
getTask方法会不断的循环,从队列中取任务
- workQueue.take:如果阻塞队列为空,当前线程会被挂起等待;当队列中有任务加入时,线程被唤醒,take方法返回任务,并执行;
- workQueue.poll:如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null;
提交任务
调用execute(Runnable command)方法把任务提交到线程池
- 线程池里的线程小于核心线程数,创建新的线程
- 队列未满(往队列里添加任务成功)
当核心线程已满,队列已满的情况下尝试创建非核心线程
首选判断线程池的状态,如果线程池的状态值大于SHUTDOWN,则不处理提交任务,直接返回
- 如果线程数量不满足要求,则直接返回不做处理。添加核心线程要求当前线程数小于核心线程数;添加非核心线程要求当前线程数小于线程池数量上限;且线程最大不能超过最大容量
利用CAS尝试修改线程个数使其+1,如果成功则直接跳出循环,开始创建线程
- 把提交的任务最为task构造worker
- 把任务添加到工作队列,在添加之前获取锁添加完成释放,防止并发造成数据异常
- 最后执行t.start()启动worker,worker会通过run方法调用外部类的runWorker方法不断的从任务队列中消费任务
创建线程池
- corePoolSize 核心线程数线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。
- 线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;
- 线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间;默认情况下,该参数只在线程数大于corePoolSize时才有用;
- keepAliveTime的单位;
- 用来保存执行任务的阻塞队列,该队列泛型限制只接收Runnable类型的元素。根据传入的队列性质不同,线程池的特性也不同。
Executors是对构造方法的封装,通过传入不同的参数,使线程池具有不同的特性,不再详细描述。
总结
- 每个worker对应一个thread,线程池通过控制worker的数量来控制并发线程数
- 线程池实质是一个生产者消费者模型,它有一个任务队列和一个消费线程集合组成。
- 任务通常会被提交到队列中,由消费线程执行。
- 运行的worker线程会通过run方法调用外部类的runWorker方法不断消费任务。
- JDK源码走读之深入理解线程池(ThreadPoolExecutor)
- jdk 线程池 threadPoolExecutor 源码剖析
- JDK源码走读之ArrayList
- JDK源码走读之LinkedList
- JDK源码走读之ReentrantLock
- 深入理解java线程池—ThreadPoolExecutor
- 深入理解java线程池—ThreadPoolExecutor
- 深入理解java线程池—ThreadPoolExecutor
- 深入理解java线程池—ThreadPoolExecutor
- 深入理解java线程池—ThreadPoolExecutor
- ThreadPoolExecutor JDK线程池
- 0031 Java线程池(ThreadPoolExecutor)JDK源码解析【基础】
- JDK 源码解析 —— Executors ExecutorService ThreadPoolExecutor 线程池
- ThreadPoolExecutor线程池理解
- jdk线程池-ThreadPoolExecutor机制
- ThreadPoolExecutor线程池代码理解
- ThreadPoolExecutor线程池源码解读
- 线程池ThreadPoolExecutor源码解析
- 香农诞辰百年纪念特辑 | 为什么说纪念香农如同纪念牛顿?
- zhong于来了?全民男神宋仲基或将成为vivo全新代言人
- VR创业前景广阔 高新技术企业抢滩国内市场
- 网络搜索引擎-正则表达式
- ReplicationExecutor的实现原理
- JDK源码走读之深入理解线程池(ThreadPoolExecutor)
- 数据库笔记1————数据系统概述
- nmap的操作与使用(学习笔记)
- Travis CI -- 免费的集成测试环境
- FIS
- 价格乱战之后,企业应该以什么样的姿态选择云存储产品?
- 有人讲真话:Unity CEO表示2016年不是VR元年
- 2020工业机器人产量要达到10万台? 这真不是闹着玩的
- 带上Xplay5去旅行,极速拍照留住最美瞬间