为什么要使用线程池

线程的创建消耗大量资源和时间,在利用线程池,则会大大的提高了资源的利用率,提高性能。

线程池是一种池化技术,通过提前创建一批线程并缓存起来,当有一个任务来执行时,从线程池中选择一个空闲线程来执行任务。

池化技术能够减少资源对象的创建次数,提高程序的性能,特别是在高并发下这种提高更加明显。简单点来说,就是提前保存大量的资源,以备不时之需。

池化技术创建的资源的特点:对象创建时间长、对象创建需要大量资源、对象创建后可被重复使用

扩展知识

Executors

Executors 的创建线程池的方法,创建出来的线程池都实现了ExecutorService的接口

  1. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程
  2. newCachedThreadPool: 创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,缓存型池子通常用于执行一些生存期很短的异步型任务
  3. newScheduledThreadPool: 创建一个定长线程池,支持定时及周期性任务执行
  4. scheduleAtFixedRate: 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推
  5. scheduleWithFixedDelay: 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟
  6. newSingleThreadExecutor: 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
  7. 自定义线程池
 ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1024), new ThreadPoolExecutor.AbortPolicy());
}

线程池核心原理

通常,一般构造函数会反映这个工具的基本的数据存储结构

image.png

  • corePoolSize: 核心线程数,启动线程后,驻留的线程
  • maximumPoolSize: 最大线程数,核心线程数都在执行时,会开启生成临时线程,maximumPoolSize = corePoolSize + 临时线程数
  • workQueue:任务等待队列,当执行任务的线程数超过maximumPoolSize时,会将任务放入到队列中,等待被执行
  • keepAliveTime: 临时线程空闲多长时间被释放掉
  • threadFactory: 创建线程的工厂,用于设置线程的自己的属性
  • handler: 拒绝策略,当任务都超过了等待队列的执行时,会抛出拒绝异常
添加一个任务

使用线程池调用execute() 执行任务的方法,其实就是将任务添加到线程池中。
image.png

  • 第一步:判断工作线程 小于核心线程数 则添加一个线程任务。成功则直接返回,没有则会进入第二步
  • 第二步:线程池非关闭,添加任务到任务队列中,再次判断线程池状态,因为此时需要添加一个非核心线程任务去启动了。
  • 第三步:添加一个非核心线程任务处理,不成功是则拒绝该任务

:::tips
为什么会反复的检查线程池的状态,因为在任务之后,可能当前线程池shutDown或者stop了,那么这个时候,就需要拒绝该任务。要意识到这一点,在多线程中,某些状态值会经常的发生变化,需要时常去关注。
:::

:::tips
这里有一个问题,如果核心线程数为0时,线程池会怎样?任务依旧会被执行,此时任务会先添加到队列,然后去判断是否可以启动非核心线程去执行任务。
:::

添加Worker线程

addWorker(Runnable firstTask, boolean core) 主要是添加线程
image.png

  1. 判断线程池是否为启动,非启动状态,直接返回
  2. 自旋的方式,添加线程数,如果线程数超过了最大线程或核心线程,则直接返回

image.png

  1. 创建Worker线程对象
  2. 获取线程池全局锁
  3. 添加worker线程到workers队列中(线程池)
  4. 添加成功则执行线程
Worker线程执行任务

image.png

  1. 先执行firstTask任务,firstTask任务为空时,则冲worksQueue队列中阻塞时获取任务
  2. 线程池非启动状态,或线程非中断
  3. 执行任务之前的钩子
  4. 执行任务
  5. 任务之后之后的钩子

总结

  1. 调用execute(),如果还有核心线程数,则直接添加Worker,否则会将任务添加到队列中
  2. 调用addWorker(), new 一个Worker对象,并调用worker.run方法执行任务
  3. worker线程任务不断去拉取任务执行

处理流程:

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值