【多线程】线程池详解

news2024/11/22 16:01:15

文章目录

  • 什么是线程池
    • 生活案例理解线程池
    • 为什么使用线程池
    • 线程池的优点
  • 自定义线程池
  • 内置线程池
  • 总结

什么是线程池

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象;
线程池是一种池化的技术实现,池化技术的核心思想就是实现资源的复用,避免资源的重复创建和销毁带来的性能开销。线程池可以管理一堆线程,让线程执行完任务之后不进行销毁,而是继续去处理其它线程已经提交的任务。

生活案例理解线程池

参数说明:
● 核心线程数量:当有一个任务提交到线程池中的时候,如果当前运行的线程数量没有达到核心线程数 量,那么就会新开一个线程去执行。(允许创建出来的线程数量)
● 最大线程数量:控制程序中最大的线程数量
● 最大空闲时间:线程在不执行的时候,最大允许存活的时间,达到最大空闲时间之后,就会回收线程
● 任务队列:当达到核心线程之后,后来的线程就会加到任务队列中,任务队列加满之后,就会按照设定的最大线程数来创建线程

通过案例理解上面的各个参数:
客户(任务)去银行(线程池)办理业务,但银行刚开始营业,窗口服务员还未就位(相当于线程池中初始线程数量为0),
于是经理(线程池管理者)就安排1号工作人员(创建1号线程执行任务)接待客户(创建线程):
在客户业务还没办完时,b客户(任务)又来了,于是经理(线程池管理者)就安排2号工作人员(创建2号线程执行任务)接待扑客户(仅创健了一个新的线程)
假设该银行总共就2个窗口(核心线程数量是2):
紧接着在,b客户都没有结束的情况下c客户来了,于是经理(线程池管理者)就安排c客户先坐到银行大厅的座位上(空位相当于是任务队列等候
并告知他:如果1、2号工作人员空出,c就可以前去办理业务:
此时d客户又到了银行,(工作人员都在忙,大厅座位也满了)于是经理赶紧安排临时工(新创建的线程)在大堂站着,手持却d设备给d客户办理业务,
假如前面的业务都没有结束的时候客户又来了,此时正式工作人员都上了,临时工也上了,座位也满了(临时工加正式员工的总数量就是最大线程数),
于是经理只能按《超出银行最大接待能力处理办法》(饱和处理机制)拒接接待客户:
最后,进来办业务的人少了,大厅的临时工空闲时间也超过了1个小时(最大空闲时间),经理就会让这部分空闲的员工人下班(销毁线程)
但是为了保证银行银行正常工作(有一个allowCoreThreadTimeout变量控制是否允许销毁核心线程,默认false),即使正式工闲着,也不得提前下班,所
以1、2号工作人员继续待着(池内保持核心线程数量):

为什么使用线程池

使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力;当然了,使用线程池的原因不仅仅只有这些,我们可以从线程池自身的优点上来进一步了解线程池的好处:

线程池的优点

1:线程和任务分离,提升线程重用性:
2:控制线程并发数量,降低服务器压力,统一管理所有线程;
3:提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线
程用的时间为T3,那么使用线程池就免去了T1和T3的时间;

4降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
5提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
6提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

自定义线程池

MyWorker类:线程执行的逻辑,用于保存线程的名字

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类:自定义线程池

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

    //提交任务
    public void submit(Runnable r){
        //判断当前集合中任务数量,是否超出了最大任务数量
        if (tasks.size()>=workSize){
            System.out.println("任务:"+r+"被丢弃了……");
        }else{
            tasks.add(r);
            //执行任务
            execTask(r);
        }
    }
    //执行任务
    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+"被缓存了……");
        }
    }

}

MyTask:该类为任务类,包含任务编号,每一个任务执行时间设计为0.2s

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

测试类:

public class MyTest {
    public static void main(String[] args) {
        //创建线程池对象
        MyThreadPool pool=new MyThreadPool(2,4,20);
        //提交多个任务
        for (int i = 0; i < 10; i++) {
            //创建任务对象,并提交给线程池
            MyTask my=new MyTask(i);
            pool.submit(my);
        }
    }
}

在这里插入图片描述
可以修改测试类中的任务个数,可以看到一些线程被丢弃了
在这里插入图片描述

内置线程池

ExecutorService接口是java内置的线程池接口,通过学习接口中的方法,可以快速的掌握java内置线程池的基本使用
常用方法:
void shutdown()启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
ListshutdownNow()停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
Futuresubmit(Callabletask)执行带返回值的任务,返回一个Future对象。
Future<?>submit(Runnable task)执行Runnable任务,并返回一个表示该任务的Future.
Futuresubmit(Runnable task,T result)执行Runnable任务,并返▣一个表示该任务的Future。

public class MyTest {
    public static void main(String[] args) {
//        extracted();
        test2();
    }
//练习newCachedThreadPool方法
    private static void extracted() {
        //使用工厂类获取线程池对象
        ExecutorService es=Executors.newCachedThreadPool();
        //提交任务
        for (int i = 0; i < 10; i++) {
            es.submit(new MyRunnable(i));
        }
    }

    private static void test2() {
        //使用工厂类获取线程池对象
        ExecutorService es=Executors.newCachedThreadPool(new ThreadFactory() {
            int n=1;
            @Override
            public Thread newThread(Runnable r) {
               return new Thread(r,"自定义的下称名称"+n++);
            }
        });
        //提交任务
        for (int i = 0; i < 10; i++) {
            es.submit(new MyRunnable(i));
        }
    }
}
//任务类,包含一个任务编号,在任务中打印出是哪一个线程正在执行任务
  class MyRunnable implements Runnable{
    private int id;

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

     @Override
     public void run() {
        //获取线程的名称,打印一句话
         String name=Thread.currentThread().getName();
         System.out.println(name+"执行了任务……"+id);
     }
 }

总结

线程池是一种常用的多线程编程技术,通过预先创建一定数量的线程并管理它们的生命周期,可以避免频繁创建和销毁线程带来的开销,同时也能够控制线程的数量和执行顺序,提高系统的性能和稳定性。

在使用线程池时,需要根据实际需求选择不同的线程池类型和参数配置。常用的线程池类型包括FixedThreadPool、CachedThreadPool、SingleThreadExecutor等,每种类型都有其特点和适用场景。此外,还需要关注线程池的参数配置,如核心线程数、最大线程数、任务队列、线程存活时间等等。

除了基本的线程池功能,还可以通过拓展线程池来满足特定的需求,如定时任务、异步任务、优先级任务等。在拓展线程池时,可以使用ThreadPoolExecutor或者自定义ThreadPoolExecutor的子类,并重写其中的方法来实现额外的功能。

总之,线程池是一种重要的多线程编程技术,可以有效地提高系统的性能和稳定性。在使用线程池时,需要根据实际需求选择不同的线程池类型和参数配置,并注意线程安全问题和线程池的调优和监控。

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

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

相关文章

【经验分享】gemini-pro和gemini-pro-vision使用体验

Gemini Gemini已经对开发者开放了Gemini Pro的使用权限&#xff0c;目前对大家都是免费的&#xff0c;每分钟限制60条&#xff0c;至少这比起CloseAI的每个账户5刀限速1min3条要香的多&#xff0c;目前已于第一时间进行了体验 一句话总结&#xff0c;google很大方&#xff0c;但…

影视动画行业发展现状与方向:AI技术推动动画工业化体系新变革

工业化体系 是国产动画强国的必经之路 中国动画的百年历程不仅是创作者们展现艺术才华的历程&#xff0c;也是一代代中国动画人不懈追求动画工业体系建设的历程。 为什么现在的中国动画需要建立工业化体系呢&#xff1f; 举个例子&#xff0c;在建立工业化体系之前&#xff…

Colorful Grid Codeforces Round 910 (Div. 2) C

Problem - C - Codeforces 题目大意&#xff1a;有一个n*m的网格&#xff0c;要求从(1,1)走到(n,m)&#xff0c;同时要求路径的长度必须为k1&#xff0c;然后给每个两点之间的路径染成红色或蓝色&#xff0c;要求任意两个相邻线段颜色不能相同&#xff0c;求涂色的方案 3<…

MSF学习

之前的渗透测试中 其实很少用到 cs msf 但是在实际内网的时候 可以发现 msf cs 都是很好用的 所以现在我来学习一下 msf的使用方法 kali自带msf https://www.cnblogs.com/bmjoker/p/10051014.html 使用 msfconsole 启动即可 首先就是最正常的木马生成 所以这里其实只需…

Selenium三大等待(详解版)

一、强制等待 1.设置完等待后不管有没有找到元素&#xff0c;都会执行等待&#xff0c;等待结束后才会执行下一步 2.实例&#xff1a; driver webdriver.Chrome()driver.get("https://www.baidu.com")time.sleep(3) # 设置强制等待driver.quit() 二、隐性等待 …

【Spring】04 国际化

文章目录 1. 定义2. Spring 的支持1&#xff09; MessageSource接口2&#xff09; ResourceBundleMessageSource 3. 配置国际化1&#xff09;配置MessageSource Bean2&#xff09;创建资源文件3&#xff09;在Bean中使用国际化消息 4. 使用占位符和参数结语 Spring 为我们提供了…

深入理解C语言的函数参数

1、一个简单的函数 int Add(int x, int y) {return x y; }int main() {printf("%d", Add(2, 3, 4, 5, 6));return 0; } 这一段足够简单的代码&#xff0c;闭眼都能知道运行结果会在屏幕上打印 5 。那编译器是怎么处理后面的 4、5、6 &#xff1f; 我们再看看这个函…

【系统架构】集群、分布式概念及系统架构演进过程

集群、分布式概念&#xff1a; 对食物没有太高要求的人在肚子饿的时候一般都会选择去兰州拉面、沙县小吃等小饭馆&#xff0c;这类小饭馆有个很显著的特点&#xff1a;洗菜、切菜、炒菜都是同一个人完成&#xff0c;如果厨子不舒服可能饭馆还会歇业。而一些人流量较大的饭馆的分…

【Qt之QNetworkAccessManager】概述及示例

概述 QNetworkAccessManager类允许应用程序发送网络请求和接收应答 网络访问API是围绕一个QNetworkAccessManager对象构建的&#xff0c;该对象为它发送的请求保存通用配置和设置。它包含代理和缓存配置&#xff0c;以及与此类问题相关的信号&#xff0c;以及可用于监视网络操…

【Linux API 揭秘】container_of函数详解

我的圈子&#xff1a; 高级工程师聚集地 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强企业&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; …

leetcode-21-合并两个有序链表(C语言实现)

题目&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出…

不用再找,这是大模型 LLM 微调经验最全总结

大家好&#xff0c;今天对大模型微调项目代码进行了重构&#xff0c;支持ChatGLM和ChatGLM2模型微调的切换&#xff0c;增加了代码的可读性&#xff0c;并且支持Freeze方法、Lora方法、P-Tuning方法、「全量参数方法」 微调。 PS&#xff1a;在对Chat类模型进行SFT时&#xff…

一款计算机顶会爬取解析系统 paper info

一款计算机顶会爬取解析系统 paper info 背景项目实现的功能 技术方案架构设计项目使用的技术选型 使用方法本地项目部署使用ChatGPT等大模型创建一个ChatGPT助手使用阿里云 顶会数据量 百度网盘pfd文件json文件 Q&A github链接 &#xff1a;https://github.com/codebricki…

PyTorch 模型训练性能大揭秘:从基础到高级技巧一网打尽!

PyTorch 是一个开源的 Python 机器学习库&#xff0c;基于Torch&#xff0c;用于自然语言处理等应用程序。 PyTorch既可以看作加入了GPU支持的numpy&#xff0c;也可以看成一个拥有自动求导功能的强大的深度神经网络&#xff0c;同时它也是大模型开发的首选工具。 《PyTorch模…

每日分享,以元旦为题的诗词

元旦佳节即将来临&#xff0c;相信大家都会在朋友圈表达一下自己的情感&#xff0c;不管大家以前是怎么表达的&#xff0c;今天小编给你分享几首以元旦为题的几首诗&#xff0c;喜欢的朋友可以自取&#xff0c;想要更多免费的诗词&#xff0c;请自行百度或小程序搜索&#xff1…

ES中根据主键_id查询记录

一、需求 es中_type&#xff1a;_doc&#xff0c;想要根据主键_id查询记录 二、实现 复合查询中使用语句查询http://192.168.1.1/_doc/1

ArcGIS for Android开发引入arcgis100.15.2

最后再点击同步即可&#xff01;&#xff01;&#xff01;

Stable diffusion 简介

Stable diffusion 是 CompVis、Stability AI、LAION、Runway 等公司研发的一个文生图模型&#xff0c;将 AI 图像生成提高到了全新高度&#xff0c;其效果和影响不亚于 Open AI 发布 ChatGPT。Stable diffusion 没有单独发布论文&#xff0c;而是基于 CVPR 2022 Oral —— 潜扩…

如何用开关电源测试系统测试电源峰值电流?

一、用万用表、示波器测量峰值电流 首先将待测电路输入信号线分别连接到测试电路的输入端和地端。待测电路的电源端连接电源。然后将示波器设置为AC耦合模式&#xff0c;通道1连接待测电路输入端&#xff0c;通道2连接待测电路地端。调整数字万用表为电流测量模式。打开电源&am…

【动手学深度学习】(十三)深度学习硬件

文章目录 一、CPU和GPU二、更多的芯片1.DSP:数字信号处理2.可编程阵列(FPGA)3.AI ASIC 三、单机多卡并行 一、CPU和GPU 提升CPU利用率 在计算ab之前&#xff0c;需要准备数据 主内存->L3->L2->L1->寄存器(数据只有进入寄存器才可以参与运算) 提升空间和时间的内存…