十、线程池

回顾以前的连接池概念
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用

线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度

特点:

  • 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
  • 提高响应速度: 当任务到达时,任务可以不需要等待线程创建就能立即执行。
  • 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

[

](https://blog.csdn.net/weixin_47872288/article/details/119453092)
具体架构:
Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor 这几个类
image.png
说明:Executors为工具类,I为接口类,C为实现类

10.1 种类与创建

  • Executors.newFixedThreadPool(int)一池N线程

    1. ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //5个窗口
  • Executors.newSingleThreadExecutor()一池一线程

    1. ExecutorService threadPool2 = Executors.newSingleThreadExecutor(); //一个窗口
  • Executors.newCachedThreadPool()一池可扩容根据需求创建线程

    1. ExecutorService threadPool3 = Executors.newCachedThreadPool();

    执行线程execute()
    关闭线程shutdown()
    线程池中的执行方法execute源代码为 ```java public interface Executor {

    /**

    • Executes the given command at some time in the future. The command
    • may execute in a new thread, in a pooled thread, or in the calling
    • thread, at the discretion of the {@code Executor} implementation. *
    • @param command the runnable task
    • @throws RejectedExecutionException if this task cannot be
    • accepted for execution
    • @throws NullPointerException if command is null */ void execute(Runnable command); }
  1. void execute(Runnable command);参数为Runnable接口类,可以通过设置lambda
  2. **具体案例代码案例**
  3. ```java
  4. //演示线程池三种常用分类
  5. public class ThreadPoolDemo1 {
  6. public static void main(String[] args) {
  7. //一池五线程
  8. ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //5个窗口
  9. //一池一线程
  10. ExecutorService threadPool2 = Executors.newSingleThreadExecutor(); //一个窗口
  11. //一池可扩容线程
  12. ExecutorService threadPool3 = Executors.newCachedThreadPool();
  13. //10个顾客请求
  14. try {
  15. for (int i = 1; i <=10; i++) {
  16. //执行
  17. threadPool3.execute(()->{
  18. System.out.println(Thread.currentThread().getName()+" 办理业务");
  19. });
  20. }
  21. }catch (Exception e) {
  22. e.printStackTrace();
  23. }finally {
  24. //关闭
  25. threadPool3.shutdown();
  26. }
  27. }
  28. }

10.2 底层原理

通过查看上面三种方式创建对象的类源代码
都有new ThreadPoolExecutor
具体查看该类的源代码,涉及七个参数

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue,
  6. ThreadFactory threadFactory,
  7. RejectedExecutionHandler handler) {
  8. if (corePoolSize < 0 ||
  9. maximumPoolSize <= 0 ||
  10. maximumPoolSize < corePoolSize ||
  11. keepAliveTime < 0)
  12. throw new IllegalArgumentException();
  13. if (workQueue == null || threadFactory == null || handler == null)
  14. throw new NullPointerException();
  15. this.corePoolSize = corePoolSize;
  16. this.maximumPoolSize = maximumPoolSize;
  17. this.workQueue = workQueue;
  18. this.keepAliveTime = unit.toNanos(keepAliveTime);
  19. this.threadFactory = threadFactory;
  20. this.handler = handler;
  21. }

具体代码中的七个参数讲解:
int corePoolSize, 常驻线程数量(核心)
int maximumPoolSize,最大线程数量
long keepAliveTime,TimeUnit unit,线程存活时间
BlockingQueue workQueue,阻塞队列(排队的线程放入)
ThreadFactory threadFactory,线程工厂,用于创建线程
RejectedExecutionHandler handler拒绝测试(线程满了)

具体工作流程是:

  • 在执行创建对象的时候不会创建线程
  • 创建线程的时候execute()才会创建
  • 先到常驻线程,满了之后再到阻塞队列进行等待,阻塞队列满了之后,在往外扩容线程,扩容线程不能大于最大线程数。大于最大线程数和阻塞队列之和后,会执行拒绝策略。

阻塞队列为3,常驻线程数2,最大线程数5
image.png
具体的拒绝策略有:

  1. 抛异常
  2. 谁调用找谁
  3. 抛弃最久执行当前
  4. 不理不问

image.png

10.3 自定义线程池

实际在开发中不允许使用Executors创建,而是通过ThreadPoolExecutor的方式,规避资源耗尽风险
image.png

  1. ExecutorService threadPool = new ThreadPoolExecutor(
  2. 2,
  3. 5,
  4. 2L,
  5. TimeUnit.SECONDS,
  6. new ArrayBlockingQueue<>(3),
  7. Executors.defaultThreadFactory(),
  8. new ThreadPoolExecutor.AbortPolicy()
  9. );

其他都同理,只是调用ThreadPoolExecutor类,自定义参数
完整代码演示

  1. //自定义线程池创建
  2. public class ThreadPoolDemo2 {
  3. public static void main(String[] args) {
  4. ExecutorService threadPool = new ThreadPoolExecutor(
  5. 2,
  6. 5,
  7. 2L,
  8. TimeUnit.SECONDS,
  9. new ArrayBlockingQueue<>(3),
  10. Executors.defaultThreadFactory(),
  11. new ThreadPoolExecutor.AbortPolicy()
  12. );
  13. //10个顾客请求
  14. try {
  15. for (int i = 1; i <=10; i++) {
  16. //执行
  17. threadPool.execute(()->{
  18. System.out.println(Thread.currentThread().getName()+" 办理业务");
  19. });
  20. }
  21. }catch (Exception e) {
  22. e.printStackTrace();
  23. }finally {
  24. //关闭
  25. threadPool.shutdown();
  26. }
  27. }
  28. }