并发编程(十六):线程池概述

目录


学习资料

《Java并发编程的艺术》第9章


1.简介

线程池好处:

  • 降低资源消耗:线程创建销毁会消耗资源
  • 提高响应速度
  • 便于管理线程

2.线程池实现原理

线程池要素:

  • corePool:核心线程池大小
  • maximunPool:最大线程池大小
  • BlockingQueue<Runnable>:任务(阻塞)队列,四种
    • ArrayBlockingQueue:数组实现有界阻塞队列,FIFO
    • LinkedBlockingQueue:基于链表阻塞队列,FIFO,吞吐量高于ArrayBlockingQueue
    • SynchronousQueue:不存储元素的阻塞队列,吞吐量高于LinkedBlockingQueue
    • PriorityBlockingQueue:一个具有优先级的阻塞队列
  • RejectedExecutionHandler:拒绝(饱和)策略,四种
    • AbortPolicy:抛出异常
    • CallerRunsPolicy:用当前线程运行任务
    • DiscardOldestPolicy:丢弃队列里最近一个任务,并执行当前任务
    • DiscardPolicy:不处理,丢掉

线程执行任务时会经历4个阶段:(阻塞队列不同可能具体行为不同)

  1. 核心:当前运行的线程少于corePoolSize,新建线程并执行
  2. 阻塞:当前的线程数>=corePoolSize,将任务加入BlockingQueue
  3. 最大:无法加入BlockingQueue,创建新的线程来处理任务(需要获取全局锁)
  4. 拒绝:如果创建新线程将使当前运行的线程超出maximumPoolSize,则拒绝,调用拒绝策略

ThreadPoolException中执行任务示意图:

线程执行任务分两种情况:

  • 创建线程时提交的任务
  • 反复从BlockingQueue获取任务执行

3.线程池的使用

3.1 创建线程池

七个参数:

  • corePoolSize:核心线程池数量,调用prstartAllCoreThreads()方法会提前启动所有线程
  • maximumPoolSize:最大线程池数量,无界队列该参数没效果
  • keepAliveTime:当前的线程数量大于corePoolSize,指定时间后超过的空闲线程销毁
  • unit:keepAliveTime的单位
  • workQueue:任务队列,4种
  • threadFactory:线程工厂
  • handler:拒绝策略,4种

3.2 提交任务

两个方法:

  • execute(runnable):提交不需要返回值的任务
  • submit(runnable):提交需要返回值的任务
    • 会返回一个future类型的对象
    • 可以通过future.get()来阻塞当前线程,直到返回工作线程结果
    • 可以使用超时方法get(long timeout,TimeUnit unit)

3.3 关闭线程池

两种方式:shutdown()shutdownNow(),原理都是 遍历+interrupt方法

  • shutdown():不会停止正在执行的线程,常用
  • shutdownNow():会尝试停止所有线程,不常用

两个判断方法:

  • isShutDown():是否调用了上面两个方法之一
  • isTerminated():是否所有线程停止

3.4 配置线程池

分析任务特性:

  • 性质:CPU密集型,IO密集型还是混合型
  • 优先级:高,中,低
  • 执行时间:长,中,短
  • 依赖性:是否依赖其他资源,如数据库

性质不同规模的线程池分开处理:

  • CPU密集型:线程数配置尽可能小,如 CPU个数+1

  • IO密集型:应配置尽可能多的线程,如 2*CPU数

  • 混合型任务:拆分为一个CPU密集型和一个IO密集型,如果执行时间相差很大,不要拆

  • 获取CPU数量:

    Runtime.getRuntime().avaliableProcessors();
    

    依赖数据库连接池的任务,等待时间越长,CPU空闲时间越长,线程数应设置越大

    建议使用有界队列,能增加系统稳定性和预警能力

3.5 线程池监控

系统中使用了大量线程池,可以通过线程池提供的参数进行监控,可以get以下属性:

  • taskCount:线程池需要执行的任务数量
  • completedTaskCount:已完成的任务数量
  • largestPoolSize:线程池里曾经创建过的最大线程数量
  • getPoolSize:线程池的线程数量
  • getActiveCount:获取活动的线程数

也可以扩展线程池方法,重写以下方法:

  • beforeExecute(..):执行前
  • afterExecute(..):执行后
  • terminated(..):关闭前

4.学习总结

1.线程池简介

  • 是什么
  • 好处:降低资源消耗,提高响应速度,便于管理线程

2.线程池实现原理
线程池组成:

  • corePool:核心池
  • maximumPool:最大池
  • BlockingQueue:阻塞队列 (四种)
    • ArrayBlockingQueue, LinkedBlockingQueue
    • SynchronousQueue(不存储元素)
    • PriorityBlockingQueue(优先级)
  • 饱和拒绝策略:(四种)
    抛出异常,当前线程执行,丢弃前一个,直接丢弃

线程池执行四个阶段:
核心池—>阻塞—>最大池—>拒绝

3.线程池的使用

  1. 创建线程池:七个参数
    两个大小(核心池,最大池),两个时间(空闲时间,单位),工厂,阻塞队列,拒绝
  2. 提交任务:
    • execute():无返回值
    • submit():返回future
  3. 关闭线程池
    • shutdown():正常终止,执行完队列中的任务
    • shutdownNow():立即终止
    • isShutDown():是否调用了上方法
    • isTerminated():是否所有线程停止
  4. 配置线程池
    • 分析任务特性:IO密集,CPU密集, 混合型
    • 性质不同分配不同数量线程:
      • CPU密集型:少,cpu数+1
      • IO密集型:多,2倍cpu数
    • 获取CPU数:
      Runtime.getRuntime().avaliableProcessors();
    • 建议使用有界队列
  5. 线程池监控
    • 监控参数:
      • taskCount:线程池需要执行的任务数量
      • completedTaskCount:已完成的任务数量
      • largestPoolSize:线程池里曾经创建过的最大线程数量
      • getPoolSize:线程池的线程数量
      • getActiveCount:获取活动的线程数
    • 扩展:
      • beforeExecute(..):执行前
      • afterExecute(..):执行后
      • terminated(..):关闭前
原文地址:https://www.cnblogs.com/kenshine/p/14520644.html