[Java] 线程池初识

线程池

    • 线程池
      • 前言
      • 线程池的参数介绍
      • 因为线程池过于复杂,于是Java标准库提供了更便捷的Executor类
      • 线程池的工作过程(模拟线程池)

线程池

前言

池,我们听过有很多,诸如:常量池、数据库连接池、线程池、进程池、内存池…
池的作用就是提高效率

池的核心思路不外乎两种:

  1. 提前把要用的对象准备好
  2. 用完的对象也不要立即释放,留在池子里,以备下次使用

    就好像拼乐高积木一样,你会把你常用的一些零件放在一个盒子里,这样拼的时候就非常方便拿取,效率会快很多。这里的 “盒子”,就是 “

为什么会有池化技术的出现?

最开始,进程的出现解决了 “并发编程“ 问题。但是由于频繁创建、销毁进程,成本过高,所以引入了 轻量级进程 -> 线程
但是如果创建、销毁线程的频率进一步提高,此时的开销也不能忽略掉

所以为了解决上述问题,有两种解决方案:

  1. 引入 轻量级线程 -> 纤程(或称 “协程”)
    • 协程本质上是程序猿在 用户态代码 中进行调度,而不是靠内核的调度器,这就节省了很多在调度上的开销(操作系统内核中是没有协程的)
    • 在用户代码中,协程是基于线程封装出来的,其底层实现有不同方案:
      • 可能是N个协程对应一个线程;也可能是N个协程对应M个线程
  2. 线程池。把要使用的线程提前创建好,用完了也不要直接释放,而是保存,以备下次使用。这就节省了线程创建与销毁的开销
    • 为什么从线程池里取线程,就比从系统中申请更高效呢?

    打个比方,现在你要去当地某办事处办某个证,你可以选择在小程序上办理,也可以直接去当地政府办事处办理。你在小程序上办理的速度是由你来控制的,就非常的快;但如果你去当地办事处交给工作人员办理,那就不好说什么时候办好了

    • 所以说从线程池取线程,就是比从系统中申请更高效

线程池的参数介绍

标准库中提供了一个叫:ThreadPoolExecutor类
这个类的构造方法有很多参数,其参数侧面反映了线程池的设计思路,所以我们要去了解。
其构造方法有四个版本,我们用参数最多的那个版本来讲解(这个版本参数是最全的,了解完这个版本其他的也就都了解了)
![[Pasted image 20231224170257.png]]

  1. 第一个参数 int corePoolSize核心线程数

    • Java线程池中会 长期保持 这个数量的线程
  2. 第二个参数 int maximumPoolSize最大线程数
    标准库提供的线程池,所持有的线程个数,并非是一成不变的,会根据当前任务量自适应线程个数(任务非常多,就多搞几个线程;任务比较少,就少搞几个线程)
    并不是说第一个参数是 最小线程数

    打个比方,第一个参数相当于正式工,第二个参数相当于临时工。任务少的时候开除临时工降低成本,但是正式工是不能随便开除的;任务多时再招几个临时工便可。

  3. 第三个参数 long keepAliveTime:存活时间

  4. 第四个参数 TimeUnit unit:时间单位(ms,s,min,h…)
    这两个参数是一起用的,第一个不要理解错误,这是 “临时工” 线程允许最大的空闲(摸鱼)时间,超过了这个时间阈值,这个线程就会被销毁掉(除核心线程之外多加的那些临时线程)

  5. 第五个参数 BlockingQueue workQueue:和定时器类似,线程池中也可以池有多个任务,也可以设置成 PriorityBlockingQueue,这就带有优先级

    • Runnable 就是描述这个任务,这个之前已经写过很多遍了,不再赘述
  6. 第六个参数 ThreadFactory threadFactory:线程工厂

    • 线程工厂,就相当于在这个类里面提供了方法(当然,也不一定非得是静态方法),让这个方法封装了 new Thread 这个操作,并且同时给 Thread 设置一些属性。此时就构成了 ThreadFactory
    • 总结:这个线程工厂就是用来创建线程的。工厂,就是以流水线的方式生产东西。
  7. 第七个参数[重要] RejectedExecutionHandler handler拒绝策略

    • 在线程池中,有一个阻塞队列,其能够容纳的元素是有 上限 的。当任务队列已经满了,如果继续往队列中添加任务,那么线程池会怎么办?

有以下四个策略:

  1. 如果继续添加任务,那么直接就抛出异常(旧任务和新任务,爷都不干了~直接撂挑子)
  2. 新的任务,由添加任务的线程负责执行(谁揽的活谁去干,反正爷不去)
  3. 丢弃最老的任务,添加新的任务(这个好理解,我能干10个活,我把我手中最老的那个任务扔了,正好空出一个任务容量, 把这个任务添加进来)
  4. 丢弃最新的任务(谁也别干了,爷不干,把这个新任务添加进来的那个线程也不干,都不干~)直接把这个新的任务扔掉
    [Java] 线程池初识_第1张图片

因为线程池过于复杂,于是Java标准库提供了更便捷的Executor类

ThreadPoolExecutor 本身比较复杂,因此标准库还提供了另一个版本,把这个类封装了一下,称为 Executors
通过这个类来创建不同的线程池对象(在内部把 ThreadPoolExecutor 创建好了,并且设置了不同的参数)
[Java] 线程池初识_第2张图片
演示:
[Java] 线程池初识_第3张图片

其核心方法就是 submit()
[Java] 线程池初识_第4张图片


线程池的工作过程(模拟线程池)

模拟一个 固定线程数目的线程池(暂时不考虑线程的增加和减少)
思路分为以下四步:

  1. 提供构造方法,指定创建出多少个线程
  2. 在构造方法中,把这些线程都创建好
  3. 提供一个阻塞队列,能够持有要执行的任务
  4. 提供 submit() 方法,可以添加新的任务

[Java] 线程池初识_第5张图片
[Java] 线程池初识_第6张图片

你可能感兴趣的:(java,apache,开发语言)