您现在的位置是:首页 > 正文

自定义线程池

2024-01-30 21:33:13阅读 0

一:参数分析

我们要想自定义线程池,必须先了解线程池的工作流程,才能自己定义线程池。下图是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个参数。

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

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

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

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

二:代码实现

1:编写任务类(MyTask),实现Runnable接口;
2:编写线程类(MyWorker),用于执行任务,需要持有所有任务;
3:编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;
4:编写测试类(MyTest),创建线程池对象,提交多个任务测试;

MyTask.java:

package com.eyes.thread.threadPool.demo1;

public class MyTask implements Runnable{
    private int 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 +
                '}';
    }
}

MyWorker.java:

package com.eyes.thread.threadPool.demo1;

import java.util.List;

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();
        }
    }
}

MyThreadPool.java:

package com.eyes.thread.threadPool.demo1;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

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 + "被缓存了...");
        }
    }
}

MyTest.java:

package com.eyes.thread.threadPool.demo1;

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);
        }
    }
}

运行结果:
在这里插入图片描述

如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:瞳孔的个人空间

网站文章

  • MobaXterm登录密码重置问题

    MobaXterm登录密码重置问题

    登录MobaXterm提示输入密码,密码输入多次后无果,密码忘记如法使用MobaXterm软件,经过查询后可采用密码重置的方式处理。使用浏览器打开如下网址:https://mobaxterm.moba...

    2024-01-30 21:33:04
  • 括号匹配数据结构

    学习分享本周学习的是数据结构的括号匹配,所谓括号匹配指的是在命令端输入一行只含有括号的代码,然后运行代码,判断每一个左括号是否有一个右括号与之对应,从而判断输入的数据是否违法代码如下:#define ...

    2024-01-30 21:32:57
  • echart图表保存为图片的两种方式

    echart图表保存为图片的两种方式

    将echarts图表和ucharts图表保存为图片

    2024-01-30 21:32:28
  • 头歌大数据——MapReduce 基础实战 答案 无解析

    头歌大数据——MapReduce 基础实战 答案 无解析

    2024-01-30 21:32:20
  • JMeter巧用计数器实现CSV数据文件设置的功能

    JMeter巧用计数器实现CSV数据文件设置的功能

    需求本次压测范围包含登录接口,但是压测环境user表用户数据量太少,和生产环境数据量不是一个量级,因此,需要先通过并发跑注册接口造用户数据需要参数化的字段是username和phone说明:本次演示的接口是示例接口,非实际生产环境接口注册接口如下:方案一:CSV 数据文件设置我们先通过代码(python或者java均可)造一定量的参数化数据写在参数化reg.txt文件中pac...

    2024-01-30 21:32:13
  • android线,android_线

    android线,android_线

    说明:android螺纹。android无非就是一个线程Main Thread和Worker Thread。(除了主线程Main Thread是Worker Thread)Main Thread 也叫...

    2024-01-30 21:31:35
  • HMM与CRF模型的使用过程存在哪些差异?

    HMM与CRF模型的使用过程存在哪些差异?

    训练后, 我们就得到了具备预测能力的新模型: lambda = CRF(w1, w2, ..., wn), 其中的模型参数已经改变.之后给定输入序列(x1, x2, ..., xn), 经过模型计算l...

    2024-01-30 21:31:28
  • Java读取文件为字符串】使用Java编程读取文件内容并将其作为字符串进行处理

    在Java中,读取文件内容需要使用文件输入流(FileInputStream)和缓冲区输入流(BufferedReader)。这是一个简单的示例,展示了如何使用Java读取文件并将其作为字符串进行处理...

    2024-01-30 21:31:21
  • flash制作车轮转动的汽车沿着路径走的动画

    flash制作车轮转动的汽车沿着路径走的动画

    二维动画制作实验报告一.实验目的1.掌握动画的概念。2.熟练Flash的界面。3.掌握Flash界面中各组成元素和功能。二.实验工具    Flash三.实验要求制作车轮转动的汽车沿着路径走。四.实验...

    2024-01-30 21:30:51
  • lattice-arc-post计算边上累计得分

    lattice-arc-post计算边上累计得分

    1 模型预测产生lat.1.gz这里请参考理解lattice,这里有介绍解码过程中如何产生lattice内容文件,以及lattice内容分析。2 边上累计得分gunzip -c 20200921.la...

    2024-01-30 21:30:45