线程池的使用及实现

news2024/11/17 13:50:02

使用多进程进行并发编程,会频繁的创建销毁进程,效率比较慢,所以引入了线程,线程使用复用资源的方式提高了创建销毁的效率,但是随着创建线程的频率进一步提高,开销仍然无法忽略不计了。

要想办法优化此处线程的创建销毁效率,方案有两种:

  1. 引入轻量级线程——纤程/协程。即Java 21里引入的”虚拟线程“。协程的本质是程序员在用户态代码中进行调度,不是靠内核的调度器调度的,节省了很多调度上的开销。
  2. 线程池。把要使用的线程提前创建好,用完了不销毁等待下次使用。每次创建一个新线程需要为该线程分配堆栈内存、初始化线程管理数据结构等等,这些操作都需要消耗一定的系统资源。使用线程池可以重复利用已经创建的线程,避免了这种开销。

1. Java标准库中的线程池

1.1 ThreadPoolExecutor类

 ThreadPoolExecutor 类提供了如下四个构造方法:

我们重点理解最后一个:

  1. corePoolSize(核心线程数):这是线程池中始终保持活动状态的线程数量。即使没有任务需要执行,这些线程也会保持活跃,可以理解为最小线程数。
  2. maximumPoolSize(最大线程数):最大线程池大小。当提交的任务数大于核心线程池大小并且工作队列已满时,线程池会创建新线程来处理任务,但新线程数量不会超过最大线程池大小。
  3. keepAliveTime(非核心线程空闲时间):非核心线程空闲时间。当线程池中的线程数量大于核心线程池大小时,如果线程空闲时间超过了该参数所指定的时间,那么这个线程就会被销毁,直到线程数量等于核心线程池大小。
  4. unit(时间单位):keepAliveTime参数的时间单位,可以是秒、毫秒等。
  5. workQueue(工作队列):任务队列。用于存储尚未执行的任务。线程池会从任务队列中取出任务并进行处理。
  6. threadFactory(线程工厂):用于创建新线程的工厂。可以通过自定义线程工厂来设置线程的名称、优先级等属性。
  7. handler(拒绝策略):当线程池无法处理新提交的任务时,将使用此策略来处理。常见的拒绝策略有:
    AbortPolicy:直接抛出异常,不处理任务。
    CallerRunsPolicy:只用调用者所在的线程来运行任务。
    DiscardOldestPolicy:丢弃队列中最旧的任务,然后尝试重新提交当前任务。
    DiscardPolicy:直接丢弃任务,不处理。

需要注意的是,corePoolSize和maximumPoolSize参数决定了线程池的容量大小,而workQueue则决定了能够存储多少个等待执行的任务。如果任务量过大,超出了workQueue的容量,再加上全部的线程都在执行任务的情况下,那么就会触发线程池的拒绝策略来处理这些任务,从而保证线程池不会因为资源被耗尽而崩溃。 

解释:

工厂模式:工厂模式是一种常见的设计模式,通过专门的  工厂类 / 工厂对象 来创建指定的对象,例如这里的 ThreadFactory。

工厂模式本质上是为了给Java语法填坑的,举个例子:

我要表示平面上的一个点,可以用笛卡尔坐标系,也可以用极坐标系:

 

 很明显,这样的代码无法通过编译,因为,这两个构造方法无法构成重载。

 为了解决上述问题,就引入了工厂模式,使用普通的方法来创建对象,就是把构造方法封装了一层:

此时这两个方法就叫工厂方法,如果把工厂方法放到其他的类里,这个类就叫工厂类,总的来说,通过静态方法封装new 操作,在方法内部设定不同的属性完成对象初始化,构造对象的过程就是工厂模式。

1.2 Executors 工厂类

ThreadPoolExecutor 类本身用起来比较复杂,所以标志库中还提供了另一个版本,把ThreadPoolExecutor 给封装了一下。即 Executors 工厂类,通过这个类来创建出不同的线程池对象(在内部把ThreadPoolExecutor 创建好了,并设置了不同的参数)。

 

SingleThreadExecutor:只包含单个线程的线程池
ScheduledThreadPool:定时器类似物,能延时执行任务
CachedThreadPool:线程数目能动态扩容
FixedThreadPool:线程数目固定

使用示例:

public class ThreadDemo28 {
    public static void main(String[] args) {
        //创建一个四个线程的线程池
        ExecutorService service = Executors.newFixedThreadPool(4);
        //通过submit方法添加任务
        service.submit(() -> {
            System.out.println("Ting");
        });
    }
}

ThreaPoolExecutor  也是通过submit 添加任务,只是构造方法不同。

希望高度定制化时使用 ThreadPoolExecutor 

 创建线程池的时候,怎么设置线程池的线程数量比较合适?

这个情况需要具体问题具体分析:

一个线程是CPU密集型任务,还是 IO 密集型任务

CPU密集型任务:这个线程大部分都在CPU上执行,如果所有线程都是CPU密集型的,这个时候建议线程数量不要大于设备的逻辑核心数量。

IO 密集型任务:这个线程大部分时间都在等待 IO ,如果所有线程都是 IO 密集型的,这个时候线程数量可以很多

上述两种情况是极端情况,大部分情况都是,有一部分线程是 CPU 密集型,一部分是 IO 密集型,所以,更适合的做法是通过测试的方式找到合适的线程数目。 

即尝试给线程池设定不同的线程数目分别进行性能测试,对比每种线程数目下,总的时间开销,和系统资源占用的 开销,找到一个最合适的值。

 

2. 简单实现一个线程池

 我们先来整理一下,实现一个线程池需要哪些内容:

  • 一个任务队列:记录要执行的任务
  • submit方法:添加任务
  • 构造方法:指定线程的数量以及创建,运行线程

 

class MyTreadPoolExecutor {
    //任务队列,这里使用一个阻塞队列
    private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);

    //构造方法
    public MyTreadPoolExecutor(int num) {
        for(int i = 0; i < num; i++) {
            //创建线程
            Thread t = new Thread(() -> {
                //循环取出任务并执行
                while(true) {
                    try {
                        queue.take().run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }

    //submit
    public void submit(Runnable runnable) {
        //添加任务
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
public class TreadDemo29 {
    public static void main(String[] args) throws InterruptedException {
        MyTreadPoolExecutor pool = new MyTreadPoolExecutor(4);

        for(int i = 0; i < 1000; i++) {
            int n = i;
            pool.submit(() -> {
                System.out.println("线程:" + Thread.currentThread().getName() + "执行了任务:" + n);
            });
        }
    }
}

运行效果:

注意这里的任务执行无序的原因是,多个线程并发执行。

例如:任务 0 刚被某个线程拿到,改线程就被调度出了cpu 此次任务 2 就可能被拿到并执行了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1295248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

防火墙是什么?聊聊部署Web应用防火墙的作用

数字经济时代&#xff0c;也是一个应用爆炸的时代。在享受应用带来的便利同时&#xff0c;当前却出现许多热点威胁&#xff0c;如供应链安全、零日漏洞、数据泄露等&#xff0c;都给现代化应用带来严峻挑战。有了WAF防火墙的帮助&#xff0c;就可以拦截一系列企图通过入侵系统来…

区块链如何影响数字营销的各个方面?

在过去的几年里&#xff0c;由于区块链等新技术和趋势的进步&#xff0c;数字营销领域发生了各种变化和发展。区块链是加密货币爱好者和投资者当前的流行语。然而&#xff0c;它的可能性已经超出了加密货币的世界&#xff0c;今天&#xff0c;来自不同行业的组织正在获得他们的…

目标检测器技术演进简史

引言 目标检测算法的发展已经取得了长足的进步&#xff0c;从早期的计算机视觉方法开始&#xff0c;通过深度学习达到了很高的准确度。在这篇博文中&#xff0c;我们将一起回顾一下这些算法的发展阶段以及现代目标检测系统中使用的主要方法。 我们首先回顾早期传统的目标检测…

服务器端模板注入 (SSTI) 漏洞实战与技巧,网络高级工具透明代理的几种实现方式

服务器端模板注入 (SSTI) 漏洞实战与技巧,网络高级工具透明代理的几种实现方式。 SSTI(Server-Side Template Injection)从名字可以看出即是服务器端模板注入。比如python的flask、php的thinkphp、java的spring等框架一般都采用MVC的模式,用户的输入先进入Controller控制器,…

内网渗透基础

内网 内网指的是内部局域网&#xff0c;常说的LAN&#xff08;local area network&#xff09;。常见家庭wifi网络和小型的企业网络&#xff0c;通常内部计算机直接访问路由器设备&#xff0c;路由器设备接入移动电信的光纤实现上网。 内部局域网可以通过交换机/防火墙组成多个…

华清远见嵌入式学习——QT——作业2

作业要求&#xff1a; 代码运行效果图&#xff1a; 登录失败 和 最小化 和 取消登录 登录成功 和 X号退出 代码&#xff1a; ①&#xff1a;头文件 #ifndef LOGIN_H #define LOGIN_H#include <QMainWindow> #include <QLineEdit> //行编辑器类 #include…

《深入理解计算机系统》学习笔记 - 第四课 - 机器级别的程序

Lecture 05 Machine Level Programming I Basics 机器级别的程序 文章目录 Lecture 05 Machine Level Programming I Basics 机器级别的程序intel 处理器的历史和体系结构芯片的构成AMD 公司(Advanced Micro Devices&#xff0c;先进的微型设备) C, 汇编, 机器代码定义汇编/机器…

MDK官网如何下载stm32支持包

网站&#xff1a;https://www.keil.com/demo/eval/arm.htm 1 2 3点这个下载

打造专属小程序,乔拓云模板平台助力商家抢占先机

打造专属小程序&#xff0c;乔拓云模板平台助力商家抢占先机&#xff01;该平台涵盖全行业小程序模板&#xff0c;一键复制即可上线。 想要快速创建高效实用的小程序&#xff0c;乔拓云小程序模板开发平台为您提供了解决方案&#xff01;我们为您提供一系列精心设计的小程序模板…

智汇恒星科技|控乐屋.全宅智能冠军代言来啦, 智慧家居千亿蓝海

随着5G、大数据、云计算、物联网等技术的发展&#xff0c;智能化正覆盖人们生活的方方面面&#xff0c;全屋智能的出现为“一键式”智能家居生活享受提供无限可能。近年来智能家居行业总体规模增长迅速&#xff0c;数据显示&#xff0c;2022年中国智能家居行业市场规模约为6200…

flstudio21破解汉化版2024最新水果编曲使用教程

​ 如果你一直梦想制作自己的音乐(无论是作为一名制作人还是艺术家)&#xff0c;你可能会想你出生在这个时代是你的幸运星。这个水果圈工作室和上一版之间的改进水平确实令人钦佩。这仅仅是FL Studio 21所提供的皮毛。你的音乐项目的选择真的会让你大吃一惊。你以前从未有过这…

vue3移动端脚手架(纯净,集成丰富)

概述 一个纯净的移动端框架 &#xff0c;用到了 Vue3 vuex Vite3 Vant3 sass eslint stylelint htmlhint husky commitlint axios axios-adapter VConsole 自定义全局 loading &#xff0c;自定义函数式 dialog &#xff08;api模仿微信小程序&#xff09;&#x…

选自《洛谷深入浅出进阶篇》——欧拉函数+欧拉定理+扩展欧拉定理

欧拉函数&#xff1a; 欧拉函数定义&#xff1a; 1~n中与n互质的数的个数。 比如 欧拉函数是积性函数&#xff1a;&#xff08;也就是&#xff09;当 n与m互质的时候&#xff1a; 由算术基本定理&#xff0c;我们可以设n&#xff0c;那么我们只要计算出的取值就能求出的取…

【后端学前端学习记录】学习计划

1、个人背景 写了足够久的后端了&#xff0c;常用的语言基本上都接触过&#xff0c;没有在工作中写过前端 一直想做一些前端的工作&#xff0c;但是前端技能不足加上自己审美不行&#xff0c;写出的界面总是很丑 所以一直对前端做不好&#xff0c;也没有真正下手。 2、动机 种…

C# Solidworks二次开发:选择管理器相关的API介绍

今天在讲述主要内容之前&#xff0c;先说一个不太相关的问题。 我之前在其他文章中看到有一些朋友在问为什么获取到的点位数据需要乘以1000进行单位转换&#xff0c;其实原因是这样的&#xff0c;在所有使用的API中如果没有特殊说明&#xff0c;所有的长度单位都是米&#xff…

Langchain-Chatchat大语言模型本地知识库的踩坑、部署、使用

Langchain-Chatchat的部署 Langchain-Chatchat概述实现原理 开发环境准备软件要求硬件要求 部署拉取仓库创建虚拟环境安装全部依赖初始化配置文件初始化知识库模型下载启动项目启动API服务启动Web UI服务使用对话功能知识库管理文件对话搜索引擎问答 异常集合异常1异常2异常3 L…

Linux——web网站服务(一)

一、安装httpd服务器Apache网站服务 1、准备工作 为了避免发送端口冲突&#xff0c;程序冲突等现象&#xff0c;卸载使用rpm方式安装的httpd #使用命令检查是否下载了httpd [rootserver ~]# rpm -qa httpd #如果有则使用 [rootserver ~]# rpm -e httpd --nodeps Apache的配置…

AGI 时代,小红书邀您一起复盘年度 100 件值得学习的好案例

2023 年&#xff0c;由 ChatGPT 引爆新一轮人工智能热潮&#xff0c;开启了由大模型驱动的 AGI 时代。随着 AIGC 的快速崛起&#xff0c;传统的研发模式是否会被 AI 颠覆也成为了巨大的未知数。面对不同规模团队与不同业务场景&#xff0c;企业想要找到可靠、可行、可落地的转型…

temu日本站在哪里入驻

在跨境电商领域中&#xff0c;Temu是拼多多推出的一款备受瞩目的平台。如今&#xff0c;越来越多的商家希望将自己的业务扩展到日本市场&#xff0c;而在Temu日本站上入驻就成为了一个不可忽视的机遇。本文将为您介绍如何在Temu日本站上入驻&#xff0c;并提供一些有用的技巧和…

Swing程序设计(9)复选框,下拉框

文章目录 前言一、复选框二、下拉框总结 前言 该篇文章简单介绍了Java中Swing组件里的复选框组件、列表框组件、下拉框组件&#xff0c;这些在系统中都是常用的组件。 一、复选框 复选框&#xff08;JCheckBox&#xff09;在Swing组件中的使用也非常广泛&#xff0c;一个方形方…