此文章的内容来源于博主看《并发编程的艺术》这本书所作下的笔记 希望对大家有帮助QAQ
线程池的好处
- 降低资源消耗。通过重复利用已经创建的线程降低线程创建和消耗造成的消耗
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就立刻执行
- 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性
线程池的实现原理
———————————————————————————————————————
- 步骤1(判断核心线程池是否为满)和步骤3(判断线程池是否已满)需要获取全局锁(将会是一个严重地可伸缩瓶颈)所以在ThreadPoolExecutor会先完成预热(不满足步骤一的条件,使其进入到队列中)
- 线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,会循环获取工作队列里的任务来执行
- 线程池的线程执行任务分成两种情况
-
- 在execute()方法中创建一个线程时,会让这个线程执行当前任务
- 这个线程执行完后,会反复从BlockingQueue中获取任务执行
线程池的创建
首先来看构造方法
接着来解释各个参数
- corePoolSize:线程池的基本大小,当提交一个任务到线程池的时候,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务(只要不达到线程池基本大小他就会)也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。
- maximumPoolSize:线程池最大数量:线程允许创建的最大线程数,如果队列满了,并且已创建的线程小于最大线程数,则线程池会在创建新的线程执行任务。如果使用了无界的任务队列,那么这个参数就没有什么效果
- keepAliveTime:线程没有被执行时的最大存活时间
- unit:时间单位
- workQueue: 用于等待和保存等待执行的任务的阻塞队列(这个后面会对几个队列进行介绍)
- handler:饱和策略
向线程池提交任务
- execute()方法用于提交不需要返回值地任务,所以无法判断任务是否被线程池执行成功
- submit()方法用于提交需要返回值的任务。线程池返回一个future对象,通过这个future对象可以判断到任务是否执行成功,get()方法会阻塞当前线程直到任务完成
线程池的关闭
shutdown()和shutdownNow()
- shutdown()
-
- 将线程池的状态设置为shutdown,然后尝试停止所有没有正在执行的线程
- shutdownNow()
-
- 将线程池的状态设置为STOP,然后尝试停止所有正在执行或者暂停的线程
只要调用了这两个方法的其中一个执行了,那么调用isShutdown就会返回true,当所有任务都关闭以后,才表示线程池关闭成功,调用isTerminaed就会返回true
合理配置线程池
- 任务的性质:CPU密集型任务、IO密集型任务和混合型任务
-
- CPU密集型任务应配置尽可能小的线程,如Ncpu+1个线程的线程池
- IO密集型任务可以配置2*Ncpu混合型的任务
- 任务的优先级:高、中和低
-
- 优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理,如果一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行
- 任务的执行时间:长、中和短
- 任务的依赖性:是否依赖其他系统资源,如数据连接
-
- 依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU。
- 建议使用有界队列,有界队列能增加系统的稳定性和预警能力,无界队列可能会撑满内存,导致整个系统不可用。
线程的监控
通过扩展线程池进行监控
- 继承线程池来自定义线程池,重写线程池地beforeExecute、afterExecute、terminated方法
- 在任务执行前、执行后和线程池关闭的时候执行一些代码来进行监控
这一篇就此打住了 初步了解了一下 !!!!
下一篇 我们来了解线程池的框架Executor