并发编程----4.java并发包中线程池的原理研究

并发编程----4.java并发包中线程池的原理研究

java并发包中线程池ThreadPoolExecutor的原理研究

线程池的优点:线程的复用,减少线程创建和销毁带来的消耗;提供了一种资源限制和线程管理的手段,比如限制线程的个数和动态新增线程等。线程池继承 AbstractExecutorService

介绍
  1. ctl:原子变量,用来记录线程中的线程池状态和线程个数,类似于ReentrantReadWriteLock使用一个变量state保存两种信息

    1. 高三位表示线程池的状态(Integer 32位)
    2. 29位记录线程池的个数
  2. 线程池的状态

    1. shutdown:拒绝新的任务但是处理阻塞队列中的任务
    2. stop:拒绝新任务并且抛弃阻塞队列中的任务,同时中断正在处理的任务
    3. tidying:所有的任务执行完了之后包括阻塞队列中的线程,将调用terminated
    4. terminated:终止状态
    5. running:接受任务并且处理阻塞队列中的任务
  3. 线程状态转化

    1. running–>shutdown
    2. runining/shutdown–>stop
    3. shutdown–>tidying
    4. stop–>tidying
    5. tidying–>terminated
  4. 线程池参数

    1. corePoolSize: 线程池核心线程数
    2. workQueue: 用于保存等待执行任务的阻塞队列,例如:数组有界ArrayBlockingQueue、基于链表的LinkedBlockingQueue、最多一个元素的SynchronousQueue,优先队列PriorityBlockingQueue
    3. maximunPoolSize:线程池最大线程数量
    4. ThreadFactory:创建线程工厂
    5. RejectedExecutionHandler:饱和策略,当队列满且线程数达到MaximumPoolSize后采取的策略,比如抛出异常,AbortPolicy;CallerRunsPolicy调用者所在的线程运行任务。DiscardOldestPolicy丢弃一个任务执行当前任务。DiscardPolicy默默抛弃不抛出异常
    6. keeyAliveTime:存活时间,当前线程比核心线程多,并且处于闲置状态,则这些闲置的线程能存活最大时间
  5. 线程池类型

    1. newFixedThreadPool
      1. 创建一个核心数和最大线程数都为n的线程池,并且阻塞队列为Integer.MAX_VALUE。keekAliveTime=0说明线程个数比核心线程多并且空闲则回收
    2. newSingleThreadExecutor
      1. 创建一个核心数和最大线程数都为1的线程池,并且阻塞队列为Integer.MAX_VALUE。keekAliveTime=0说明线程个数比核心线程多并且空闲则回收
    3. newCacheThreadPool
      1. 按需创建线程的线程池,初始线程个数为0.最多线程数为Integer.MAX_VALUE,并且阻塞队列为同步队列,keepAliveTime=60说明只要任务只要在60s内就被回收。当加入同步任务就会马上被执行,同步队列中最多只有一个任务
        在这里插入图片描述
  6. mainLock为独占锁,用来控制新增worker线程操作原子性,termination是该锁对应的条件队列,在线程调用个awaitTermination时存放阻塞的线程。

  7. Work继承AQS和Runnable接口,是具体承载任务的对象,简单实现了不可重入锁,其中state=0为锁未被获取状态,state=1表示获取状态-1为默认。firstTask执行的第一个任务。

  8. DefaultThreadFactory是线程工厂,newThread 方法是对线程一个修饰。其中poolNumber是个静态原子变量,用来统计线程工厂个数。threadNumber统计线程工厂创建了多少线程。

  9. 源码分析

    1. public void execute(Runnable command)提交任务
      1. 当当前线程数个数小于corePoolSize,会向workers里面新增核心线程执行该任务
      2. 当大于或者等于corePoolSize放入任务队列(workQueue)中,如果当前线程池状态不是running的话就抛弃新任务
      3. 添加任务成功,则对线程进行第二次的状态校验,这是因为任务放入任务队列后,执行代码前任务可能已经变化,这里进行校验,如果是非running状态就从队列里面移除任务,并且执行拒接策略。如果二次校验通过,重新判断线程池里面是否有线程,如果没有新增一个线程。
      4. 如果任务失败,说明任务队列已满,尝试开始新的线程,如果当前线程个数>maximumPoolSize则执行拒绝策略。
    2. 添加任务
      1. 检查队列是否只在必要的时候为空(返回情况:stop,tidying,terminated;shutdown并且队列中到一个一个任务;shutdown并且队列为空)
      2. cas增加线程个数(超出最大线程数返回),成功返回,失败查看是否是状态改变是就返回,不是就重新cas
      3. 创建worker,使用mainLock实现wokers同步,
      4. 检查状态是否被shutdown了,是就返回不是添加任务,
      5. 成功启动任务。
    3. 工作线程worker的执行
      1. 获取任务,如果任务为null则执行清理工作,如果不为空,获取该任务的锁,执行相关任务,统计worker完成了多少任务,并且释放锁(加锁的目的是为了防止其他线程调用了shutdown,阻止当前任务的执行)
      2. 清理操作,统计完成任务数量,从工作集中删除当前任务,尝试设置为terminated状态。如果当前线程<核心个数增加。
    4. shutdown操作
      1. mainLock锁住
      2. 权限检查,调用的线程师傅有关闭的权限,没有抛出异常
      3. 设置为shutdown,是直接返回,不是设置为shutdown
      4. 设置中断标志、尝试设置为terminated,设置所有空闲线程中断标志。尝试获取worker自己的锁,成功则设置中断标志,目的是为了阻塞要从任务队列中获取任务的空闲队列。方法:使用cas 设置当前线程池的状态为tidying,设置成功就执行terminated,设置为terminated,激活所有因为调用条件变量await系列方法阻塞的所有线程。
      5. 解锁
    5. shutdownNow操作
      1. 不接受新的任务也不执行任务队列中的任务,正在执行的任务会立刻被中断,该方法会返回
    6. awaitTermination操作
      1. 当前线程不会被阻塞,知道线程状态变为terminated才返回,或则等待时间超时才返回。

java并发包中线程池ScheduledThreadPoolExecutor的原理研究

指定一个延迟时间后或者定时进行任务调度执行的线程池。ScheduledThreadPoolExecutor继承了ThreadPoolExecutor并实现了ScheduledExecutorService,线程池队列是DelayedWorkQueue。

  1. 介绍
    1. state 状态:ScheduledFutureTask是具有返回任务的继承自FutureTask。FutureTask内部有一个state用来表示任务状态
      1. 初始状态:new;执行中状态:completing : 执行中状态;normal:正常运行结束状态;exceptional:运行中异常;cancelled:任务取消;interrupting: 正在中断;interrupted:已经中断。
      2. 状态装换:new–>completing -->normal;new–>completing–>exceptional;new–>canceled;new–>interrupting–>interrupted
    2. period 任务类型:
      1. period=0:任务是一次性的
      2. period<0:任务是固定延迟时间可重复执行任务
      3. period>0:任务是固定频率的定时可重复执行任务
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值