高级并发编程系列四(线程池家谱)

1.引子

假期的时间总是很快的,不知不觉国庆节已经过去了三天。今天利用早上的空闲,我们把高级并发编程系列的文章补一补,到这个系列的第四篇文章了,前面三篇我们分别分享了:基础概念核心要点入门案例

在入门案例中,我们通过Executors工具类提供的辅助方法,创建了不同类型的线程池,最后在结论部分我们说:在实际项目中,不建议通过Executors工具类来创建线程池,理由是有线程溢出或者内存溢出的风险,那么在实际项目中,我们该如何正确的创建线程池呢?需要主要考虑哪些关键要素呢?你都还记得吗?比如说:

  • 线程池中线程数量的控制
  • 任务缓冲队列的选择
  • 任务拒绝策略

这一篇让我们一起从两个角度继续深入认识线程池:juc中线程池类图、以及如何正确的创建线程池

2.案例

2.1.线程池家谱

2.1.1.类图

2.1.2.描述

在上一篇中,通过Executors工具类提供的创建线程池的辅助方法中,我们发现辅助方法内部,最终都是创建了ThreadPoolExecutor实例对象。也就是说,在juc包中,关于线程池,ThreadPoolExecutor类是主角。

同时通过idea生成的类层次图,整个线程池类图的结构主线是这样的:

  • 线程池接口:Executor

    • 在接口Executor中,只有一个执行任务的方方法:void execute(Runnable command)
    • 我们发现execute方法的参数,是一个Runnable任务
    • 该任务可以被线程池中的某个线程执行,也可以是被提交任务的线程执行
  • 线程池子接口:ExecutorService

    • 它是Executor的子接口,在其中增加了线程池治理的方法。比如说:shutdown (关闭线程池)、submit(获取任务执行结果方法)
  • 线程池抽象类:AbstractExecutorService

    • 提供了接口方法的默认共性实现
    • 是模板设计模式的体现
  • 线程池类:ThreadPoolExecutor

    • 真正的线程池实现类
    • 我们在项目中实际需要用到的线程池就是它了

2.2.创建线程池的正确姿势

2.2.1.解读ThreadPoolExecutor

从类图中,我们看到ThreadPoolExecutor是在实际项目中,需要使用到的线程池工具类。这里关于线程池的核心知识点,我们一起再进行一下回顾,便于更好的理解ThreadPoolExecutor。

关于线程池的核心知识点,你都还记得有哪些吗:

  • 为什么要使用线程池

    • 通过复用有限的资源,处理无限的任务,最大化合理利用有限资源的同时,提升任务处理的效率
  • 如何有效的控制线程池中线程的数量

    • 我们知道操作系统,对于单个进程创建线程数量是有限制的,不能无限制创建线程
    • 如果我们的任务是cpu密集型 的,建议线程最大数量设置为:cup核心数的1-2倍
    • 如果我们的任务是io密集型 的,建议线程最大数量设置为参考公式: (1 + 任务平均等待时间/任务处理时间) * cpu核心数
  • 如果线程池中的线程都在忙,且线程数量达到了最大,此时如果有新的任务,该如何有效处理

    • 在一个地方,暂时先将新任务放一放。我们称为:任务缓冲队列
    • 如果线程数量达到了最大,且任务缓冲队列已经放满,需要说:no。我们需要:合理的拒绝任务策略

结合以上线程池的核心要点,我建议你去看一下ThreadPoolExecutor的源码,争取在我们使用它之前,先要更好的认识它,对吧。下面我截一个图,方便我们先直观的认识一下它:

2.2.2.案例代码

java 复制代码
package com.anan.edu.common.newthread.pool.manual;
​
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
​
/**
 * 手动创建线程池案例
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2020/10/4 9:33
 */
public class ManualThreadPoolDemo {
​
    /**
     * 定义线程池参数:
     */
    public static final int corePoolSize = 3;// 核心线程数量
    public static final int maximumPoolSize = 5;// 最大线程数量
    public static final long keepAliveTime = 60;// 空闲线程活跃时间
    public static final TimeUnit timeUnit = TimeUnit.MILLISECONDS;// 时间单位
    public static final BlockingQueue<Runnable> blockingQueue =
            new ArrayBlockingQueue<Runnable>(100);// 任务缓冲队列
​
    public static void main(String[] args) {
        // 1.直接实例化ThreadPoolExecutor对象
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                timeUnit,
                blockingQueue);
​
        // 2.提交任务,并执行
        threadPoolExecutor.execute(new Task());
​
        // 3.释放线程池资源
        threadPoolExecutor.shutdown();
​
    }
​
}
​
/**
 * 任务
 */
class Task implements Runnable{
​
    @Override
    public void run() {
        System.out.println("当前正在处理任务线程:" + Thread.currentThread().getName());
        System.out.println("-----------------任务处理中---------------");
    }
​
}

2.2.3.测试结果

vbnet 复制代码
D:\02teach\01soft\jdk8\bin\java com.anan.edu.common.newthread.pool.manual.ManualThreadPoolDemo
当前正在处理任务线程:pool-1-thread-1
--------------------任务处理中------------------------
​
Process finished with exit code 0
相关推荐
颜淡慕潇16 分钟前
【K8S问题系列 |19 】如何解决 Pod 无法挂载 PVC问题
后端·云原生·容器·kubernetes
ProtonBase22 分钟前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
乐之者v29 分钟前
leetCode43.字符串相乘
java·数据结构·算法
suweijie7684 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿4 小时前
List深拷贝后,数据还是被串改
java
向前看-8 小时前
验证码机制
前端·后端
xlsw_8 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹8 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭9 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫9 小时前
泛型(2)
java