线程池使用之自定义线程池

2023-11-16

目录

一:Java内置线程池原理剖析

二:ThreadPoolExecutor参数详解

三:线程池工作流程总结示意图

四:自定义线程池-参数设计分析

1:核心线程数(corePoolSize)

2:任务队列长度(workQueue)

3:最大线程数(maximumPoolSize)

4:最大空闲时间(keepAliveTime)

五:自定义线程池-实现步骤

1:编写任务类(MyTask),实现Runnable接口;

2:编写线程类(MyWorker),用于执行任务,需要持有所有任务;

3:编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;

4:编写测试类(MyTest),创建线程池对象,提交多个任务测试;


一:Java内置线程池原理剖析

              我们要想自定义线程池,必须先了解线程池的工作原理,才能自己定义线程池;这里我们通过观察java中ThreadPoolExecutor的源码来学习线程池的原理

构造方法:
public ThreadPoolExecutor(int corePoolSize, //核心线程数量
                              int maximumPoolSize,//     最大线程数
                              long keepAliveTime, //       最大空闲时间
                              TimeUnit unit,         //        时间单位
                              BlockingQueue<Runnable> workQueue,   //   任务队列
                              ThreadFactory threadFactory,    // 线程工厂
                              RejectedExecutionHandler handler  //  饱和处理机制
	) 
{ ... }

二:ThreadPoolExecutor参数详解

我们可以通过下面的场景理解ThreadPoolExecutor中的各个参数;

a客户(任务)去银行(线程池)办理业务,但银行刚开始营业,窗口服务员还未就位(相当于线程池中初始线程数量为0),

于是经理(线程池管理者)就安排1号工作人员(创建1号线程执行任务)接待a客户(创建线程);

在a客户业务还没办完时,b客户(任务)又来了,于是经理(线程池管理者)就安排2号工作人员(创建2号线程执行任务)接待b客户(创建了一个新的线程);假设该银行总共就2个窗口(核心线程数量是2);

紧接着在a,b客户都没有结束的情况下c客户来了,于是经理(线程池管理者)就安排c客户先坐到银行大厅的座位上(空位相当于是任务队列)等候,

并告知他: 如果1、2号工作人员空出,c就可以前去办理业务;

此时d客户又到了银行,(工作人员都在忙,大厅座位也满了)于是经理赶紧安排临时工(新创建的线程)在大堂站着,手持pad设备给d客户办理业务;

假如前面的业务都没有结束的时候e客户又来了,此时正式工作人员都上了,临时工也上了,座位也满了(临时工加正式员工的总数量就是最大线程数),

于是经理只能按《超出银行最大接待能力处理办法》(饱和处理机制)拒接接待e客户;

最后,进来办业务的人少了,大厅的临时工空闲时间也超过了1小时(最大空闲时间),经理就会让这部分空闲的员工人下班.(销毁线程)

但是为了保证银行银行正常工作(有一个allowCoreThreadTimeout变量控制是否允许销毁核心线程,默认false),即使正式工闲着,也不得提前下班,所以1、2号工作人员继续待着(池内保持核心线程数量);

三:线程池工作流程总结示意图

四:自定义线程池-参数设计分析

             通过观察 Java 中的内置线程池参数讲解和线程池工作流程总结 , 我们不难发现 , 要设计一个好的线程池 , 就必须合理的设置线程池的 4 个参数 ; 那到底该如何合理的设计 4 个参数的值呢 ? 我们一起往下看 .
4 个参数的设计 :

1:核心线程数(corePoolSize)

  核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定,例如:执行一个任务需要0.1,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个任务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照8020原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理;

2:任务队列长度(workQueue)

  任务队列长度一般设计为:核心线程数/单个任务执行时间*2即可;例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1,则队列长度可以设计为200;

3:最大线程数(maximumPoolSize)

  最大线程数的设计除了需要参照核心线程数的条件外,还需要参照系统每秒产生的最大任务数决定:例如:上述环境中,如果系统每秒最大产生的任务是1000,那么,最大线程数=(最大任务数-任务队列长度)*单个任务执行时间;: 最大线程数=(1000-200)*0.1=80;

4:最大空闲时间(keepAliveTime)

  这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值,用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可;

上面4个参数的设置只是一般的设计原则,并不是固定的,用户也可以根据实际情况灵活调整!

五:自定义线程池-实现步骤

1:编写任务类(MyTask),实现Runnable接口;

/*
    需求:
        自定义线程池练习,这是任务类,需要实现Runnable;
        包含任务编号,每一个任务执行时间设计为0.2秒
 */
public class MyTask implements Runnable{
    private int id;
    //由于run方法是重写接口中的方法,因此id这个属性初始化可以利用构造方法完成

    public MyTask(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println("线程:"+name+" 即将执行任务:"+id);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程:"+name+" 完成了任务:"+id);
    }

    @Override
    public String toString() {
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}

2:编写线程类(MyWorker),用于执行任务,需要持有所有任务;

/*
    需求:
        编写一个线程类,需要继承Thread类,设计一个属性,用于保存线程的名字;
        设计一个集合,用于保存所有的任务;
 */
public class MyWorker extends Thread{
    private String name;//保存线程的名字
    private List<Runnable> tasks;
    //利用构造方法,给成员变量赋值

    public MyWorker(String name, List<Runnable> tasks) {
        super(name);
        this.tasks = tasks;
    }

    @Override
    public void run() {
       //判断集合中是否有任务,只要有,就一直执行任务
        while (tasks.size()>0){
            Runnable r = tasks.remove(0);
            r.run();
        }
    }
}

3:编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;

/*
    这是自定义的线程池类;

    成员变量:
        1:任务队列   集合  需要控制线程安全问题
        2:当前线程数量
        3:核心线程数量
        4:最大线程数量
        5:任务队列的长度
    成员方法
        1:提交任务;
            将任务添加到集合中,需要判断是否超出了任务总长度
        2:执行任务;
            判断当前线程的数量,决定创建核心线程还是非核心线程
 */
public class MyThreadPool {
    // 1:任务队列   集合  需要控制线程安全问题
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
    //2:当前线程数量
    private int num;
    //3:核心线程数量
    private int corePoolSize;
    //4:最大线程数量
    private int maxSize;
    //5:任务队列的长度
    private int workSize;

    public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
        this.corePoolSize = corePoolSize;
        this.maxSize = maxSize;
        this.workSize = workSize;
    }

    //1:提交任务;
    public void submit(Runnable r){
        //判断当前集合中任务的数量,是否超出了最大任务数量
        if(tasks.size()>=workSize){
            System.out.println("任务:"+r+"被丢弃了...");
        }else {
            tasks.add(r);
            //执行任务
            execTask(r);
        }
    }
    //2:执行任务;
    private void execTask(Runnable r) {
        //判断当前线程池中的线程总数量,是否超出了核心数,
        if(num < corePoolSize){
            new MyWorker("核心线程:"+num,tasks).start();
            num++;
        }else if(num < maxSize){
            new MyWorker("非核心线程:"+num,tasks).start();
            num++;
        }else {
            System.out.println("任务:"+r+" 被缓存了...");
        }
    }

}

4:编写测试类(MyTest),创建线程池对象,提交多个任务测试;

/*
    测试类:
        1: 创建线程池类对象;
        2: 提交多个任务
 */
public class MyTest {
    public static void main(String[] args) {
        //1:创建线程池类对象;
        MyThreadPool pool = new MyThreadPool(2,4,20);
        //2: 提交多个任务
        for (int i = 0; i <30 ; i++) {
            //3:创建任务对象,并提交给线程池
            MyTask my = new MyTask(i);
            pool.submit(my);
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程池使用之自定义线程池 的相关文章

随机推荐

  • java压缩包打不开_java.util.zip.ZipException: error in opening zip file 问题处理

    java util zip ZipException error in opening zip file 这个问题的字面意思是压缩包打不开 我这出现的问题是jar包损坏 打不开 linux系统可以使用命令判断jar 是否正常 jar vtf
  • Axure RP 万能的标签切换面板-动态面板切换

    切换面板在网页中算是非常常见的 如视频网站的电视剧分类标签 资讯网站的资讯分类等 这些交互效果都可以利用动态面板的几个状态来进行相互切换得来 下面来介绍一下如何制作这样的标签 步骤1 从部件库中拖拽一个动态面板到线框图编辑区中 并在 部件交
  • STM32Cube HAL库——ADC电压采集

    STM32Cube HAL库 ADC电压采集 一 ADC基本介绍 模拟数字转换器 即A D转换器 或简称ADC Analog to Digital Converter 通常是指一个将模拟信号转变为数字信号的电子元件 通常的模数转换器是将一个
  • 斗地主游戏

    牌属性类 package Java project 1 import java util Objects public class Card private String size 点数 private String color 花色 pr
  • UE4:自定义Slate 点击保存 出现USlot ReleaseSlateResources() 异常

    Error AssetLog Test uasset Leak Detected CustomWidget 43 CustomWidget still has living Slate widgets it or the parent Ca
  • pyQt5 学习笔记(19)QFrame 边框设置

    文章目录 一 QFrame 简介 二 QFrame 的创建 三 组合效果图 四 QFrame 的功能 API 1 功能 2 QFrame 结构 3 使用总结 1 挑选目标样式 2 根据参数写程序 五 信号 一 QFrame 简介 QFram
  • mysql 数组_MySQL如何实现数组功能

    前段时间想要用数组功能实现某些需求 结果发现mysql不支持数组 这个确实让人很头痛 查阅官方文档 也没有这一方面的资料 结果在网上 看到了某仁兄贴出了变相实现的一种方法 代码如下 DELIMITER DROP DATABASE IF EX
  • 【Ubuntu】使用grub2挂载NFS根文件系统(rootfs)

    经常调试arm开发板的同学应该比较熟悉uboot 它可以使用tftp自动下载内核并通过nfs挂载rootfs 其实 作为x86平台常见的bootloader grub也可以做到通过nfs挂载rootfs 安装grub2 目前新版的ubunt
  • 云vscode搭建--使用容器化部署

    Vscode作为一个轻量级的IDE 其支持丰富的插件 而通过这些插件我们就可以实现在Vscode中写任何语言的代码 Code Server是Vscode的网页版 启动Code Server之后我们就可以在浏览器中打开vscode来愉快的编写
  • java 分页page_java分页PageHelper的使用

    我以前写分页都是些工具类后面偶然的机会发现了这个pagehelper在github上开源框架 maven依赖 com github pagehelper pagehelper 5 1 6 只需要在service层加上然后在controlle
  • 网络安全单兵工具 -- YAKIT

    网络安全单兵工具 YAKIT 一 下载及安装 1 原作者及下载地址 https github com yaklang yakit 2 双击下载好的exe文件 点击核心引擎安装与升级 3 点击意见更新Yak引擎 4 以管理员启动 5 点击连接
  • 《从Paxos到ZooKeeper》读书笔记之第一章(二)

    从Paxos到ZooKeeper 读书笔记之第一章 二 1 2从ACID到CAP BASE 这一节由三小节 从大家数值的数据库事务的四个特性 引出来分布式事务的概念 通过对ACID模型的讨论 提出如何构建一个兼顾可用性和一致性的分布式系统方
  • 前端技术面试大全

    快速搞定前端技术一面 匹配大厂面试要求 迅速建立初中级前端面试 0 2年 JS知识体系 掌握解决面试题的思路与技巧 知识点介绍 CSS 布局 定位 移动端响应式 ES语法 原型 原型链 作用域 闭包 异步 单线程 Web API DOM B
  • SQL复习要点

    1 数据库系统阶段的数据管理特点 1 数据结构化2 数据共享性高 冗余度小 易扩充 3 数据独立性高4 统一的数据管理和控制功能 2 数据库 database DB 是存放数据的仓库 3 数据库管理系统 data base manageme
  • js 截取字符串里的IP和port

    例如 str 192 168 12 254 18081 1 用 ip和port 的正则 let ip reg d gi let ip ip reg exec str ip ip 0 let port reg d 1 5 let port s
  • C++,菱形继承和虚继承

    一 菱形继承的基本概念 菱形继承又称为钻石继承 由公共基类派生出多个中间子类 又由多个中间子类共同派生出汇聚子类 汇聚子类会得到 中间子类从公共基类继承下来的多份成员 菱形继承的格式 A 公共基类 B C 中间子类 D 汇聚子类 示例 in
  • 读《企业中的开放源代码》有感

    文章目录 开源代码的益处 开源的本质 开源是一门学科 开源代码的益处 学习出色编码技能的最佳方法是开发开源项目 注意是开发而不是阅读 因为可以研究该领域内顶尖编码员所采用的实践 以更快的方式启动新项目 从此没有必要再闭门造车 开源的本质 我
  • Linux平台下二进制安装FastDDS

    1 Linux平台下二进制安装 该页面主要介绍在linux环境下 使用二进制release安装 eProsima公司的 Fast DDSThe 安装 内容 运行应用 在一个CMake项目中包含Fast DDS 卸载 1 1 安装 ePros
  • 规则动词的过去式及发音规则

    规则动词的过去式词尾变化有几种 1 一般情况下加ed 如watched planted watered pulled climbed picked 2 以不发音字母e结尾的加d 如liked moved tasted 3 以辅音字母加y结尾
  • 线程池使用之自定义线程池

    目录 一 Java内置线程池原理剖析 二 ThreadPoolExecutor参数详解 三 线程池工作流程总结示意图 四 自定义线程池 参数设计分析 1 核心线程数 corePoolSize 2 任务队列长度 workQueue 3 最大线