学习多线程看我这篇就够了之线程池(一)

112 阅读4分钟

此文章的内容来源于博主看《并发编程的艺术》这本书所作下的笔记 希望对大家有帮助QAQ

线程池的好处

  • 降低资源消耗。通过重复利用已经创建的线程降低线程创建和消耗造成的消耗
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就立刻执行
  • 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性

线程池的实现原理

———————————————————————————————————————

  • 步骤1(判断核心线程池是否为满)和步骤3(判断线程池是否已满)需要获取全局锁(将会是一个严重地可伸缩瓶颈)所以在ThreadPoolExecutor会先完成预热(不满足步骤一的条件,使其进入到队列中)
  • 线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,会循环获取工作队列里的任务来执行
  • 线程池的线程执行任务分成两种情况
    • 在execute()方法中创建一个线程时,会让这个线程执行当前任务
    • 这个线程执行完后,会反复从BlockingQueue中获取任务执行

线程池的创建

首先来看构造方法

image.png 接着来解释各个参数

  • 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。
  • 建议使用有界队列,有界队列能增加系统的稳定性和预警能力,无界队列可能会撑满内存,导致整个系统不可用。

线程的监控

通过扩展线程池进行监控

  1. 继承线程池来自定义线程池,重写线程池地beforeExecute、afterExecute、terminated方法
  2. 在任务执行前、执行后和线程池关闭的时候执行一些代码来进行监控

这一篇就此打住了 初步了解了一下 !!!!

下一篇 我们来了解线程池的框架Executor