Java底层基础-线程池原理

一、引言

1.1 为什么使用线程池
  • 在Java中,如果每个请求到达就创建一个新的线程,开销会非常大。

  • 在实际使用中,服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多;如果一个JVM里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。

  • 为了防止资源不足,服务器应用程序需要采取一些方法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。

    概括起来就是: 减低资源消耗,提供响应速度,提供线程的可管理性

    ​ 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

    ​ 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。

    ​ 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

1.2 什么是阻塞队列

​ 队列:一种线性表,它的特性是先进先出,插入在一端,删除在另一端

​ 队列又分为阻塞队列BlockingQueue和非阻塞队列ConcurrentLinkedQueue

​ 它们两者的区别:

​ 非阻塞队列: 如果存放超出队列容量,则队列信息丢失;获取队列元素时,若队列为空,则返回null

​ 阻塞 队列: 如果存放超出队列容量,则进行等待(等待时间为队列设置的消费者等待超时时间);获取队列元素时,若队列为空,则进行等待。

​ 阻塞队列在操作中若规定了超时时间,但是操作不需要等待时,就不用等待;若在规定的超时时间内没有完成操作,则放弃操作。若不规定超时时间,就可以认为是非阻塞队列。

二、 线程池

2.1 线程池组成

一个比较简单的线程池至少应包含线程池管理器、工作线程、任务列队、任务接口等部分。

  • 线程池管理器 (ThreadPool) 的作用是创建、销毁并管理线程池,将工作线程放入线程池中;

  • 工作线程 (PoolWorker) 是线程池中的线程,可以循环执行任务,在没有任务时处于等待状态;

  • 任务列队 (taskQueue) 的作用是提供一种缓冲机制,将没有处理的任务放在任务列队中;

  • 任务接口 (task) 是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。

2.2 线程池的使用
public ThreadPoolExecutor(int corePoolSize,  
                              int maximumPoolSize,  
                              long keepAliveTime,  
                              TimeUnit unit,  
                              BlockingQueue<Runnable> workQueue,  
                              ThreadFactory threadFactory,  
                              RejectedExecutionHandler handler)
 

在这里插入图片描述

  • corePoolSize:核心线程数。默认情况下,核心线程会一直存活(当allowCoreThreadTimeout设置为true时会被回收)
  • maximumPoolSize: 线程池所能容纳的最大线程数。
  • keepAliveTime:线程闲置超时时长,如果超过该时长,非核心线程会被回收。如果allowCoreThreadTimeout设置为true时,核心线程也会超时回收
  • unit:指定keepAliveTime参数的时间单位
  • workQueue:等待队列,任务可以存储在此队列中等待被执行,执行的是FIFO原则。其采用阻塞队列实现
  • threadFactory(可选参数):创建线程的线程工厂
  • handler(可选参数):拒绝策略,当达到最大线程数时需要执行的饱和策略(四种拒绝策略如下)
    • AbortPolicy: 不执行新任务,直接抛出异常,提示线程池已满
    • DisCardPolicy: 不执行新任务,也不抛出异常
    • DisCardOldSetPolicy: 将等待队列中的第一个替换为当前新进来的任务执行
    • CallerRunsPolicy: 直接调用execute来执行当前任务

三、任务执行流程

3.1 添加执行任务
  • submit() 该方法返回一个Future对象,可执行带返回值的线程;或者执行想随时可以取消的线程。Future对象的get()方法获取返回值。Future对象的cancel(true/false)取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程
  • execute() 没有返回值。
3.2 任务提交过程 (工作原理)

在这里插入图片描述

  1. 首先判断核心线程数(corePoolSize),如果核心线程数未满,则创建新的线程来执行添加的任务;
  2. 如果核心线程数(corePoolSize)满了,进而判断阻塞队列(workQueue),如果阻塞队列未满,则将任务加入到阻塞队列中等待执行;
  3. 如果核心线程数(corePoolSize)满了,且阻塞队列(workQueue)也满了,则判断线程池数量(maximumPoolSize),如果线程池未满,则创建新的线程执行添加的任务;
  4. 如果核心线程数(corePoolSize)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值