「JUC篇」之 Java线程池详解

51 阅读3分钟

觉得对你有益的小伙伴记得点个赞+关注

后续完整内容持续更新中

希望一起交流的欢迎发邮件至javalyhn@163.com

1. 为什么使用线程池

  1. 降低资源消耗

    • 通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
  2. 提高响应速率

    • 因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
  3. 提高线程的可管理型

    • 线程池会根据当前系统特点,对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还会降低系统的稳定性,使用线程池进行统一分配。

2. 线程池的创建

2.1 Executors

image.png

Executors.newSingleThreadExecutor();//单线程的线程池,后台从队列里面获取任务,挨个执行
Executors.newScheduledThreadPool();//定时任务的线程池
Executors.newFixedThreadPool();//固定大小的线程池 core=max 都不可回收
Executors.newCachedThreadPool();//core是0 所有线程都可以回收

2.2 ThreadPoolExecutor(推荐使用)

image.png

2.3 ThreadPoolExecutor核心参数详解

  1. corePoolSize:核心线程数,线程池创建好以后就已经准备就绪的线程数量,就等待来接收异步任务去执行(除非设置了allowCoreThreadTimeOut,否则会一直存在

  2. maximumPoolSize:最大线程数量,控制资源

  3. keepAliveTime:存活时间。如果当前线程数量大于corePoolSize,多余的空闲线程(maximunPoolSize - corePoolSize)在终止前等待新任务的最长时间

  4. unit:时间单位

  5. BlockingQueue<Runnable> workQueue:阻塞队列。如果任务有很多,就会将目前多的任务放在任务队列里面,只要有线程空闲,就会去队列里面取出新的任务进行执行 (new LinkedBlockingQueue<>() 默认是Integer的最大值,这里可能会有内存不够的情况,所以一定要指定数量

  6. threadFactory:线程的创建工厂

  7. RejectedExecutionHandler handler:如果队列满了,就会按照我们指定的拒绝策略执行任务

image.png

  • DiscardOldestPolicy:丢弃最老的任务
  • CallerRunsPolicy:执行run方法
  • AbortPolcy:丢弃新任务并且抛出异常 (默认)
  • DiscardPolicy:丢弃新任务不抛出异常

2.4 工作顺序

  1. 线程池创建好,就会准备core数量的核心线程池准备接受任务

  2. 新的任务进来,用core准备好的空闲线程执行

    • 如果core满了,就会将接下来进入的线程放入到阻塞队列中。空闲的core就会自己去阻塞队列里面获取任务执行
    • 如果阻塞队列满了,就直接开启新的线程执行,最大只能开到指定的maximunPoolSize数量
    • 如果都已经超过maximunPoolSize了,maximunPoolSize - corePoolSize数量空闲的线程会在keepAliveTime指定的时间后自动销毁,最终保持到corePoolSize大小
    • 如果线程数开到了maxPollSize数量,还有新的任务进来,就会使用reject指定的拒绝策略进行处理
  3. 所有的线程都是由指定的factory创建的

2.5 最终展示

image.png

3. 面试题

一个线程池 core:7 max:20 queue:50 ,100并发进来怎么分配

解析:

  1. 线程池创建完成,核心线程数个数量的线程开始等待任务,因而7个线程被处理
  2. 剩下93个线程进入阻塞队列等待,当阻塞队列满了以后,再开20 - 1 = 13个线程来执行,此时还有100-7-50-13=30个线程没有被执行
  3. 剩下的30个使用拒绝策略执行(默认丢弃)