JAVA线程池总结及自定义线程池

简介: JAVA线程池总结及自定义线程池

引言


在前几年小编写过一篇关于线程池的总结:《线程总结》,现在回过头来看,总结的 还是比较详细的,不过当时并没有在项目中有过真实刺激的 体验,最近项目中偶然遇到了一次任务丢失的问题,我追踪了一下 代码, 发现由于不正当采用java内置线程池导致的, 应该是当时配置线程池的参数没有仔细计算导致的,关于这个问题我们后面博文在介绍,今天我们在看我java 内置 线程池代码以后,我们先动手自己写一个线程池来实现任务的提交和执行。这样我们可以更好的理解线程池的执行流程。如果读者对于java内置的线程的核心参数和执行流程不是很了解,可以点击上面链接,阅读博文。


一、在编写代码之前,我们先介绍几个核心参数的配置依据。


1.1、核心线程数量corePoolSize


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


1.2、任务队列长度(workQueue)


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


1.3、最大线程数(maximumPoolSize)


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


上面的 参数配置公式,都是参考作用,在实际环境中,需要根据实际服务器的配置自行调整


二、定义线程池


2.1、模拟任务类

package com.threadpoll;
/**
 * @author zhenghao
 * @description:
 * @date 2020/6/3010:07
 */
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 +
                '}';
    }
}

2.2、自定义线程类

package com.threadpoll;
import java.util.ArrayList;
import java.util.List;
/**
 * @author zhenghao
 * @description:
 * @date 2020/6/3010:13
 */
public class MyWorker extends Thread {
    private String name;
    private List<Runnable> tasks = new ArrayList<>();
    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();
        }
    }
}

2.3、自定义线程池类

package com.threadpoll;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
 * @author zhenghao
 * @description: 自定义线程池
 * @date 2020/6/3015:06
 */
public class MyThreadPool {
    /**
     * 任务集合 多个线程同时remove 
     */
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
    /**
     * 当前先测试数量
     */
    private int num;
    /**
     * 核心线程数量
     */
    private int coreThreadSize;
    /**
     * 最大线程数量
     */
    private int maxThreadSize;
    /**
     * 队列长度
     */
    private int workSize;
    public MyThreadPool( int coreThreadSize, int maxThreadSize, int workSize) {
        this.coreThreadSize = coreThreadSize;
        this.maxThreadSize = maxThreadSize;
        this.workSize = workSize;
    }
    /**
     * @Description: 提交队列
     * @author: zhenghao
     * @date: 2020/6/30 15:11
     */
    public void submitTask(Runnable r) {
        if (tasks.size() >= workSize) {
            System.out.println("任务" + r + "丢掉了");
        } else {
            //加入队列
            tasks.add(r);
            //执行队列
            execTask(r);
        }
    }
    private void execTask(Runnable r) {
        //判断是否需要创建核心线程池
        if (num < coreThreadSize) {
            //创建核心线程池执行
            new MyWorker("核心线程池" + num, tasks).start();
            num++;
        } else if (num < maxThreadSize) {
            //创建非核心线程池执行
            new MyWorker("非核心线程池" + num, tasks).start();
            num++;
        } else {
            System.out.println("任务" + r + "被缓存了");
        }
    }
}

2.4、测试类

package com.threadpoll;
/**
 * @author zhenghao
 * @description:
 * @date 2020/6/3010:07
 */
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 +
                '}';
    }
}

大家通过调解任务数量,也就是测试类中的循环数量,可以看到不同的效果,大家可以测试一下, 然后通过这个小demo,我们可以更好的了解线程池的执行流程。


目录
相关文章
|
19天前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
58 0
|
1月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
84 16
|
2月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
2月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
3月前
|
XML 人工智能 Java
java通过自定义TraceId实现简单的链路追踪
本文介绍了如何在Spring Boot项目中通过SLF4J的MDC实现日志上下文traceId追踪。内容涵盖依赖配置、拦截器实现、网关与服务间调用的traceId传递、多线程环境下的上下文同步,以及logback日志格式配置。适用于小型微服务架构的链路追踪,便于排查复杂调用场景中的问题。
126 0
|
3月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
307 83
|
3月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
144 0
|
3月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
259 83