线程池(ThreadPoolExecutor)

news2024/11/16 14:24:59

在这里插入图片描述

文章目录

  • 一、线程池
    • 标准库提供的线程池
    • ThreadPoolExecutor
    • 自定义线程池

一、线程池

为什么要引入线程池?
这个原因我们需要追溯到线程,我们线程存在的意义在于,使用进程进行并发编程太重了,所以引入了线程,因为线程又称为 “轻量级进程”,创建线程比创建进程更加有效,调度线程更加的高效。
但当我们的并发程度的提高,我们线程应该不太满足我们的要求,线程显得也不是那么的轻,那么想要提升效率,有两个解决方案:

  1. 引入"更轻量的线程",是存在这个东西的,那就是"协程/纤程",但是我们的java并没有引入这个概念,所以我们这个方案就泡汤了,相比之下Go语言在并发编程这一块就更香了,因为Go内置了协程。
  2. 使用线程池,来降低创建和销毁线程的开销。

在这里插入图片描述

肯定会有同学问,为什么引入线程池就会降低开销?
首先我们创建线程和销毁线程是由操作系统内核来控制的,我们人为无法干涉。但是如果引入线程池,我们事先就可以将线程创建好放入"线程池"中,后面需要的时候,直接从池子中取,用完归还到池子中,这样就省去了频繁的创建和销毁线程的开销。

标准库提供的线程池

JDK5.0开始,提供了代表线程池的接口: ExecutorService
如何得到线程池对象?
方式一: 使用Executors(线程池的工具类)调用方法,返回不同类型的线程池。
在这里插入图片描述
在这里插入图片描述

我们可以发现我们Executors线程池的工具类下面为我们提供了很多不同应用场景的线程池。
细心的同学可以发现,我们这里并不是直接去new 一个线程池,而是调用Executors的一个方法返回一个对象。
我们使用某个类的静态方法,直接构造出来一个对象(相当于我们的new操作隐藏在了方法里面),我们称这样的方法为 “工厂方法“,提供这个工厂方法的类,称为"工厂类”,我们这种思想就是"工厂模式” 的一种 “设计模式”.我们后面会详细介绍。

方法作用
Future<?> submit(Runnable task)向线程池提交任务
public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("使用线程池执行的第一个任务");
            }
        });
    }

在这里插入图片描述
我们可以发现我们创建了一个5个线程的线程池,我们提交了一个任务,并且顺利执行,我们可以发现我们执行完这个任务之后,程序并没有结束,因为我们的线程池再继续等待提交任务。
在这里插入图片描述
当我们想多提交几个任务并且记录是第几个任务的时候,发现程序报错了。

在这里插入图片描述
我们可以分析一下为什么我们的run方法中使用i变量会报错,因为我们的i是main方法的局部变量,而我们的run方法是属于Runnable,这个方法不是立马执行的(而是在线程池的队列中等待执行),因为有可能当我们执行到run方法的时候,循环已经执行完了,i已经被销毁了,所以我们这里的run方法无法使用i变量。
在这里插入图片描述
因为有这种作用域的差异,我们需要重新一个一个变量去记录下这个i,这个操作也称为变量捕获,也就是给run方法的栈上拷贝一份i(简单地说就是在定义run方法的时候,偷偷的把i记住,在后续执行run方法的时候,创建一个i的局部变量,并且把记录的这个值赋值过去).
在JDK1.8之前,我们变量捕获只能捕获final修饰的变量,也就是我们定义j时必须定义为final修饰。
在JDK1.8开始,我们的变量捕获不一定需要final修饰,只需要保证代码中没有修改过该变量即可。
这也就是为啥i不能捕获,因为i存在修改,而我们的j不存在修改。

public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 20; i++) {
            int j = i;
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程池执行的第: " + j + "个任务");
                }
            });
        }
    }

在这里插入图片描述
我们可以发现我们往线程池里放了20个任务,但因为我们的线程调度是随机的,所以我们这里的执行顺序不固定。
我们的20个任务由这5个线程分配一下,按道理来说是一人执行4个,但这里由于线程的调度是随机的,抢占式执行,所以这里有的多一个少一个都是很正常的。

ThreadPoolExecutor

方式二: 使用ExecutorService的实现类ThreadPoolExecutor自己创建一个线程池对象。
在这里插入图片描述
在这里插入图片描述
我们在API文档中查看一下ThreadPoolExecutor的构造方法,我们可以发现有不同参数的构造方法,我们这里介绍参数最多参数的构造方法,这个构造方法覆盖了前面几个。
在这里插入图片描述
我们再来详细的介绍一下这几个参数
1.corePoolSize(核心线程数)
我们举几个生活中的例子,我们现在创建一家公司,corePoolSize就相当于几个合伙人
2.maximumPoolSize(最大线程数)
我们的公司肯定不只是合伙人,肯定还需要一些员工,这里合伙人加员工的数量最多为最大线程数
3.keepAliveTime(最大存活时间)
我们公司肯定有活员工才有存在的价值,最大存活时间就是多长时间没有活就辞退员工
4.unit(时间单位)
最大存活时间的时间单位
5.BlockingQueue workQueue(任务队列)
我们向线程池submit的任务,就放入这个任务队列
6.ThreadFactory threadFactory(线程创建方式)
指定我们的线程池以什么方法创建线程。
7.RejectedExecutionHandler handler(拒绝策略)
我们的任务队列是有大小的,既然有大小就有存满的一刻,当我们存满时。在存入元素,会发生什么,由我们的拒绝策略决定。
这里我们举出这四种拒绝策略,以及做出的不同反应。
在这里插入图片描述

我们这里有两个比较重要的问题
1.临时线程什么时候创建?
当新任务提交时,发现核心线程都在忙,而且任务队列也满了,并且还可以创建临时线程,才会创建临时线程
2.什么时候会开始拒绝任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来时,才会开始拒绝任务。

public static void main(String[] args) {
        //创建线程池对象
        ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
    }

这里我们简单创建一个线程池对象

自定义线程池

我们的线程池主要有两个部分

  1. 任务队列,存放我们提交的任务
  2. 若干个工作线程,负责执行任务
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class MyExecutorService {
    //任务队列
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    //submit方法,向任务队列提交任务
    public void submit(Runnable runnable) {
        try {
            queue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    //创建工作线程,指定线程池中线程数量为n
    public MyExecutorService(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while (true) {
                    try {
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

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

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

相关文章

【知识图谱导论-浙大】第三、四章:知识图谱的抽取与构建

前文&#xff1a; 【知识图谱导论-浙大】第一章&#xff1a;知识图谱概论 【知识图谱导论-浙大】第二章&#xff1a;知识图谱的表示 说明&#xff1a;原视频中的第三章主要介绍了图数据库相关的内容&#xff0c;有兴趣的可以查看相关课件或者对应的视频&#xff1a; 【知识图…

[Linux理论基础1]----手写和使用json完成[序列化和反序列化]

文章目录前言一、应用层二、再谈"协议"三、 网络版计算器手写版本使用第三方库json实现完整代码总结前言 理解应用层的作用,初始HTTP协议;理解传输层的作用,深入理解TCP的各项特性和机制;对整个TCP/IP协议有系统的理解;对TCP/IP协议体系下的其他重要协议和技术有一定…

JPG格式如何转为PDF格式?快来学习如何转换

图片是我们经常用到的一种便携式文件&#xff0c;像我们日常的照片或者是一些学习资料、工作资料都是图片形式的&#xff0c;我们经常会把这些图片发送给其他人&#xff0c;这时候就需要想一个简单的办法把图片一次性发送过去&#xff0c;所以我们可以将图片转换为PDF文件&…

暨 广告、推荐、搜索 三大顶级复杂业务之 “广告业务系统详叙”

文章目录暨 广告、推荐、搜索 三大顶级复杂业务之 “广告业务系统详叙”广告系统的核心功能ADX 架构流程概述典型 ADX 架构图概述消息中心抱歉&#xff0c;有段日子没码字了&#xff0c;后面会尽量补出来分享给大家。这段时间整理了关于 “广告业务” 相关的思考&#xff0c;作…

OSPF笔记(五):OSPF虚链路--普通区域远离骨干区域

一、OSPF 虚链路 1.1 虚链路邻居关系&#xff1a; hello包只发送一次&#xff0c;没有dead时间 虚链路配置邻居指的是RID&#xff0c;非接口IP 1.2 虚链路解决的问题&#xff1a; 普通区域远离骨干区域0的问题 普通区域连接两个骨干区域0问题 &#xff08;1&#xff09;…

SpringSecurity授权功能快速上手

3. 授权 3.0 权限系统的作用 例如一个学校图书馆的管理系统&#xff0c;如果是普通学生登录就能看到借书还书相关的功能&#xff0c;不可能让他看到并且去使用添加书籍信息&#xff0c;删除书籍信息等功能。但是如果是一个图书馆管理员的账号登录了&#xff0c;应该就能看到并…

最新款发布 | 德州仪器(TI)60G单芯片毫米波雷达芯片 -xWRL6432

本文编辑&#xff1a;调皮哥的小助理 概述 最近&#xff0c;德州仪器(TI)推出了单芯片低功耗 57GHz 至 64GHz 工业(汽车)毫米波雷达传感器IWRL6432&#xff0c;具有 7GHz 的连续带宽&#xff0c;可实现更高分辨率。除了UWB雷达之外&#xff0c;IWRL6432目前是毫米波雷达带宽最…

漏洞挖掘-不安全的HTTP方法

前言&#xff1a; 年关将至&#xff0c;这可能是年前最后一篇文章了。已经有一段时间没有更新文章了&#xff0c;因为最近也没有学到什么新的知识&#xff0c;也就没什么可写的&#xff0c;又不想灌水。最近关注的好兄弟们多了很多&#xff0c;在这里也是十分感谢大家的支持&am…

Make RepVGG Greater Again | 中文翻译

性能和推理速度之间的权衡对于实际应用至关重要。而重参化可以让模型获得了更好的性能平衡&#xff0c;这也促使它正在成为现代卷积神经网络中越来越流行的架构。尽管如此&#xff0c;当需要INT8推断时&#xff0c;其量化性能通常太差&#xff0c;无法部署&#xff08;例如Imag…

SQL BETWEEN 操作符

BETWEEN 操作符用于选取介于两个值之间的数据范围内的值。 SQL BETWEEN 操作符 BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。 SQL BETWEEN 语法 SELECT column1, column2, ... FROM table_name WHERE column BETWEEN value1 AND va…

力扣714题 买卖股票的最佳时机含手续费

class Solution {public int maxProfit(int[] prices, int fee) {// 买第一天股票所需要的全部费用(买入)int buy prices[0] fee; // 利润总和int sum 0;for(int p:prices){// 如果买后些天的股票所需的全部费用比第一天的少,就买后边这天的(买入)if(p fee < buy){buy …

【Python】python深拷贝和浅拷贝(一)

【Python】python深拷贝和浅拷贝&#xff08;一&#xff09; 定义 直接赋值&#xff1a;其实就是对象的引用。浅拷贝&#xff1a;拷贝父对象&#xff0c;不会拷贝对象的内部的子对象。深拷贝&#xff1a; copy 模块的 deepcopy 方法&#xff0c;完全拷贝了父对象及其子对象。…

SpringBoot过滤器与拦截器

为什么要有过滤器和拦截器&#xff1f; 在实际开发过程中&#xff0c;经常会碰见一些比如系统启动初始化信息、统计在线人数、在线用户数、过滤敏高词汇、访问权限控制(URL级别)等业务需求。这些对于业务来说一般上是无关的&#xff0c;业务方是无需关注的&#xff0c;业务只需…

Ubuntu20.04安装ROS Noetic

一、实验环境准备 1.使用系统:Ubuntu20.04&#xff08;安装不做赘述&#xff0c;可看我另外一篇博客Ubuntu20.04安装&#xff09;&#xff0c;可到Ubuntu官网下载https://ubuntu.com/download/desktop 2.配置网络&#xff0c;使其可通互联网 二、在Ubuntu20.04上搭建ROS机器人…

树上差分-LCA

树上差分算法分析&#xff1a;练习例题差分的基本思想详情见博客&#xff08;一维、二维差分&#xff09;&#xff1a; https://blog.csdn.net/weixin_45629285/article/details/111146240 算法分析&#xff1a; 面向的对象可以是树上的结点&#xff0c;也可以是树上的边 结点…

springmvc 文件上传请求转换为MultipartFile的过程

前言: 最近在研究文件上传的问题,所以就写下这个博客,让大家都知道从流转换为MutipartFile的过程,不然你就知道在方法中使用,而不知道是怎么样处理的,是不行的 从DiaspatherServlet说起: 别问为啥,去了解tomcat和servlet的关系,我后面会 写这篇博客的 servlet的生命周期 ini…

[ 数据结构 ] 查找算法--------线性、二分、插值、斐波那契查找

0 前言 查找算法有4种: 线性查找 二分查找/折半查找 插值查找 斐波那契查找 1 线性查找 思路:线性遍历数组元素,与目标值比较,相同则返回下标 /**** param arr 给定数组* param value 目标元素值* return 返回目标元素的下标,没找到返回-1*/public static int search(…

63.Python 调用类的属性和方法

63.调用类的属性和方法 文章目录63.调用类的属性和方法1. 调用属性的语法2.调用类的方法3.总结1. 调用属性的语法 我们根据类创建了一张奥迪A6的小汽车。根据汽车流水线呢&#xff0c;汽车生产完之后&#xff0c;需要移交给检查部门检查车辆的外观、颜色(属性)等是否与图纸一致…

JavaEE高阶---SpringBoot 统一功能处理

一&#xff1a;什么是SpringBoot 统⼀功能处理 SpringBoot统一功能处理是AOP的实战环节。我们主要学习三方面内容&#xff1a; 统一用户登录权限验证&#xff1b;统一数据格式返回&#xff1b;统一异常处理。 二&#xff1a;统一用户登录权限验证 Spring 中提供了具体的实现…

Scala 集合常用函数

文章目录集合常用函数一、基本属性和常用操作1、常用方法2、案例示例二、衍生集合1、衍生集合常用方法操作2、案例示例三、集合简单计算函数1、常用计算操作2、案例示例四、集合计算高级函数1、语义说明2、案例示例集合常用函数 一、基本属性和常用操作 1、常用方法 (1) 获取…