线程池ThreadPoolExecutor使用

news2025/1/21 9:33:54

文章目录

    • 一、基础-Java中线程创建的方式
      • 1.1、继承Thread类创建线程
      • 1.2、实现Runnable接口创建线程
      • 1.3、实现Calable接口创建线程
      • 1.4、使用线程池创建线程
    • 二、概念-线程池基本概念
      • 2.1、并发和井行的主要区别
        • 2.1.1、处理任务不同
        • 2.1.2、存在不同
        • 2.1.3、CPU资源不同
      • 2.2、什么是线程池?
      • 2.3、为什么使用线程池?
    • 三、源码解读·线程池源码
      • 3.1、 线程池的核心参数
      • 3.2、线程池线程增长策略
        • 线程池创建
      • 3.3、线程池处理任务流程
      • 3.4、线程池执行过程解读
    • 四、案例-使用线程池异步处理百万级数据
      • 4.1、创建springboot项目导入依赖
      • 4.2、创建数据表
      • 4.3、配置ym
      • 4.4、创建实体类
      • 4.5、创建Mapper接口
      • 4.6、创建Service接口及实现类
      • 4.7、创建Controller
      • 4.8、创建线程池配置类
      • 4.9、创建异步任务接口及实现类
        • AsyncService.java
        • AsyncServiceImpl.java
      • 4.10、创建拆分集合的工具类
      • 4.12、改造Controller调用异步任务
    • 五、同步工具CountDownLatch解析
      • 5.1、介绍
      • 5.2、特性
      • 5.3、应用案例

一、基础-Java中线程创建的方式

1.1、继承Thread类创建线程

通过继承Thread类来创建并启动多线程的一般步骤如下
1、定义Thread类的子类,并重写该类的run0)方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2、创建Thread子类的实例,也就是创建了线程对象
3、启动线程,即调用线程的start()方法
示例:

package org.example;

/**
 * @Author Js
 * @Description
 * @Date 2024-07-30 20:35
 * @Version 1.0
 **/
public class MyThread extends Thread {
   

    @Override
    public void run() {
   
        String name = Thread.currentThread().getName();
        switch (name) {
   
            case "xcl":
                for (int i = 0; i < 100; i++) {
   
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
                break;
            case "xc2":
                for (int i = 0; i < 10; i++) {
   
                    System.out.println("好好学习java" + i);
                }
                break;
        }
    }
}


创建线程对象

package org.example;

public class Main {
   
    public static void main(String[] args) {
   
        MyThread myThread1=new MyThread();//创建线程对象
        myThread1.setName("xc1");
        myThread1.start();//启动线程
        MyThread myThread2=new MyThread();//创建线程对象
        myThread2.setName("xc2");
        myThread2.start();//启动线程
    }
}

在这里插入图片描述

1.2、实现Runnable接口创建线程

通过实现Runnable接口创建并启动线程一般步骤如下:
1、定义Runnable接口的实现类,一样要重写run0)方法,这个run()方法和Thread中的run()方法一样是线程的执行体

2、创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象

3、第二步依然三通过调田纤程对象的:ttar0方法来启动线程

package org.example;

/**
 * @Author Js
 * @Description
 * @Date 2024-07-30 20:53
 * @Version 1.0
 **/
public class MyRunable implements Runnable{
   

    @Override
    public void run() {
   
        for (int i = 0; i < 100; i++) {
   
            System.out.println(Thread.currentThread().getName()+": "+i);
        }
    }
}

创建线程对象

package org.example;

/**
 * @Author Js
 * @Description
 * @Date 2024-07-30 20:54
 * @Version 1.0
 **/
public class TestRunable {
   
    public static void main(String[] args) {
   
        Runnable myRunable=new MyRunable();//1、实例化对象
        
        Thread thread=new Thread(myRunable);//2、构建一个线程对象
        thread.setName("线程1");
        thread.start();//3、启动线程

        Thread threadl=new Thread(myRunable);
        threadl.setName("线程2");
        threadl.start();

    }
}

在这里插入图片描述

1.3、实现Calable接口创建线程

使用Callable和Future创建线程
创建并启动有返回值的线程的步骤如下:
步骤:
1、创建Callable接口的实现类,并重写call()'方法,该方法具有返回值

2、实例化实现类对象,通过实现类对象进行入参来构建一个FutureTask对象,FutureTask对象实现了Runable接口,Future也是Runable和Future接口的实现类

3、通过FutureTask对象入参构建一个线程对象,并调用start()方法来启动执行线程

4、适用FutureTask对象的get()方法来获得线程执行的结果

示例:

package org.example;

import java.util.concurrent.Callable;

/**
 * @Author Js
 * @Description
 * @Date 2024-07-30 21:01
 * @Version 1.0
 **/
public class MyCallable implements Callable<Integer> {
   
    @Override
    public Integer call() throws Exception {
   
        int num1=11;
        int num2=22;
        int sum=num1+num2;
        return sum;
    }
}

创建线程对象

package org.example;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @Author Js
 * @Description
 * @Date 2024-07-30 21:03
 * @Version 1.0
 **/
public class CallableTest {
   
    public static void main(String[] args) {
   
        MyCallable m3 = new MyCallable();//实例化对象
        FutureTask<Integer> futureTask = new FutureTask(m3); //Runable对象
        //创建线程
        Thread t1 = new Thread(futureTask);
        t1.setName("线程一");
        t1.start();
        //new Thread(futureTask,"线程一").start();
        try {
   
            System.out.println("线程的执行结果为:" + futureTask.get());//获得线程的返回值
        } catch (InterruptedException e) {
   
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
   
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

1.4、使用线程池创建线程

下面就是…

二、概念-线程池基本概念

2.1、并发和井行的主要区别

2.1.1、处理任务不同

并发(Concurrent)
并发是一个CPU处理器同时处理多个线程任务。(宏观上是同时处理多个任务,微观上其实是CPU在多个线程之间快速的交替执行。操作系统中有一个组件叫做任务调度器,它将CPU的时间片(windows下时间片最小约为15毫秒) 配给各个线程使用,在一个时间段的线程运行时,其他线程处于挂起状态,这种就称之为并发。)

并行(parallel)

并行是多个CPU处理器同时处理多个线程任务。(当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这就被称之为并行。)

2.1.2、存在不同

并发(concurrent)
并发可以在一个CPU处理器和多个CPU处理器系统中都存在。(多个CPU处理器系统其中的一个CPU也可以进行并发操作)
并行(parallel)
并行在多个CPU处理器系统存在。

2.1.3、CPU资源不同

并发(Concurrent)
并发过程中,线程之间会去抢占CPU资源,轮流使用,(其实CPU会多个各个线程公平的分配时间片和进行执行。)

并行(parallel)
并行过程中,线程间不会抢占CPU资源,(因为是多个CPU处理器,各做各的。)

2.2、什么是线程池?

首先要理解什么是线程,线程池(thread pool),是一种线程使用模式,线程池维护着多个线程,等待着监督管理者分配可并发执行的任务,就是可管理和维护以及分配线程的"池子

2.3、为什么使用线程池?

为了减少创建和销毁线程的次数,让每个线程都可以多次的使用,可以根据系统情况调整线程的数量,防止消耗过多内存。在实际使用中,服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,使用线程池就可以优化。
通俗的说:目的就是为了让线程对象可以反复的复用,不需要每次执行任务时构建一个新的线程,等到任务处理完再销毁。


三、源码解读·线程池源码

3.1、 线程池的核心参数

先看源码

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler){
   
        
    }


//参数说明:
int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程最大空闲时间,默认针对非核心线程
TimeUnit unit,//线程最大空闲时间单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//拒绝策略

3.2、线程池线程增长策略

问题:
1、当向一个新创建的线程池中提交任务时候,线程池何时创建新线程,何时把任务放入等待队列,何时执行拒绝策略?
2、当一个线程池达到最大线程后,没有任务执行,哪些线程会被销毁?
3、线程池中的最大线程可以设置为多少?
4、线程池中如何来复用一个线程?
任务提交流程

在这里插入图片描述

线程池按以下行为执行任务:
1、当线程数小于核心线程数时,创建线程。
2、当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务(阻塞)队列。
3、当线程数大于等于核心线程数,且任务队列已满,若线程数小于最大线程数,创建工作(普通)线程。
4、若线程数等于最大线程数,则执行拒绝策略

核心线程数通常设置为CPU核心数的一个合理倍数,以充分利用系统资源并提高并发能力。最大线程数则通常设置为一个合理的上眼值,避免过多的线程竞争系统资源导致性
能下降或系统崩溃。

ThreadPoolExecutor提供了四种默认的拒绝策略:
AbortPolicy(默认): 当工作队列已满并且无法再添加新任务时,抛出RejectedExecutionException异常。

CallerRunsPollcy: 当工作队列已满并且无法再添加新任务时,由提交任务的线程来执行该任务。也就是说,任务将在提交线程的上下文中执行。

DiscardOldestPolcy: 当工作队列已满并且无法再添加新任务时,丢弃队列中最早的任务(即等待时间最长的任务),然后尝试里新提交新任务。

DiscardPolicy: 当工作队列已满并且无法再添加新任务时,直接丢弃新任务,不做任何处理。


线程池创建
package org.example;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author Js
 * @Description
 * @Date 2024-07-30 22:03
 * @Version 1.0
 **/
public class ThreadPoolExecutorTest {
   
    public static void main(String[] args) {
   

        ThreadFactory factory = new ThreadFactory() {
   
			@Override
            public Thread newThread(Runnable r) {
   
                ThreadFactory threadFactory = Executors.defaultThreadFactory();
                Thread thread = threadFactory.newThread(new MyRunable());
                return thread;

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

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

相关文章

网页保护用户 小tips

在使用创建web开发的过程中&#xff0c;直接使用用户名url&#xff0c;容易造成用户信息的被攻击&#xff0c;例如对方直接访问 ../../.../username 的网页&#xff0c;可以窃取用户信息&#xff0c;然而把usename变成一堆乱码就安全的多 效果&#xff1a; 代码&#xff1a;…

一个 .NET 开源的地图组件库 - Mapsui

前言 今天大姚给大家分享一个.NET开源&#xff08;MIT License&#xff09;、免费、同时支持多平台框架&#xff08;MAUI、WPF、Avalonia、Uno、Blazor、WinUI、Eto、.NET Android 和 .NET iOS&#xff09;地图组件库&#xff1a;Mapsui。 项目源代码 支持的UI框架的NuGet包 …

JavaFX布局-TabPane

JavaFX布局-TabPane 常用属性paddingsidetabClosingPolicytabDragPolicy 实现方式Java实现fxml实现 组织一组tab的容器&#xff0c;可以设置关闭&#xff0c;拖拽等每个tab内容可以设置不同容器数据 常用属性 padding 内边距&#xff0c;可以单独设置上、下、左、右的内边距 …

webfunny埋点系统如何进行部署?

hello 大家webfunny埋点系统做了不少功能更新&#xff0c;平常给大家分享比较多的是**webfunny前端监控系统**&#xff0c;最近有不少技术同学来了解webfunny埋点系统&#xff0c;今天主要给大家分享下webfunny埋点系统部署&#xff0c;分为本地部署和线上部署。 还没有试用和…

翻译: 可视化深度学习反向传播原理一

本期我们来讲反向传播 也就是神经网络学习的核心算法 稍微回顾一下我们之前讲到哪里之后 首先我要撇开公式不提 直观地过一遍 这个算法到底在做什么 然后如果你们有人想认真看里头的数学 下一期影片我会解释这一切背后的微积分 如果你看了前两期影片 或者你已经有足够背景知…

【文件系统】磁盘的物理结构 | 存储结构

目录 0.前言 1.磁盘的物理结构 1.1什么是二进制&#xff08;0/1&#xff09; 1.2磁盘的存放位置 1.3磁盘的结构 2.磁盘的存储结构 2.1❓数据是怎样在磁盘上存储 2.2❓读写的基本单位是什么 2.3❓如何找到一个指定位置的扇区 2.4❓为什么磁盘的机械结构是这样的 0.前…

Audio Spectrogram Transformer (AST)工作介绍

Audio Spectrogram Transformer (AST)&#xff0c;是一种基于 Transformer 模型的音频分类方法。AST 利用了 Transformer 模型在捕获全局特征方面的优势&#xff0c;将音频信号转换为频谱图进行处理。下面是对 AST 及其相关研究工作的详细介绍&#xff1a; 1.研究背景 传统的音…

SpringAOP-底层实现源码解析

目录 1. Spring AOP原理流程图 2. 动态代理 3. ProxyFactory 4. Advice的分类 5. Advisor的理解 6. 创建代理对象的方式 ProxyFactoryBean BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator 7. 对Spring AOP的理解 8. AOP中的概念 9. Advice在Spring AOP中…

文心智能体【MBTI速测小精灵】:趣味速测,精准解析你的性格密码!

文章目录 一、文心智能体平台是什么&#xff1f;二、创建文心智能体智能体创建智能体调试分析智能体基础配置智能体高级配置智能体高级调试 三、文心智能体发布四、文心智能体体验总结 一、文心智能体平台是什么&#xff1f; AgentBuilder文心智能体平台是基于文心大模型的智能…

饮料加速稳定性试验

饮料加速稳定性试验概览 饮料加速稳定性试验是一种通过模拟加速条件来预测饮料在市场销售期间稳定性的测试方法。这种测试对于确保饮料产品在整个保质期内的质量和安全性至关重要。它可以帮助生产企业优化产品配方、改进包装材料、调整储存和运输条件&#xff0c;从而确保产品在…

苍穹外面day13(day10)---订单状态定时处理、来单提醒和客户催单

Spring Task 同学们可以看我这篇文章 Spring Task初学-CSDN博客 订单状态定时处理 新建OrderTask /*** 定时任务类&#xff0c;定时处理订单状态*/ Component Slf4j public class OrderTask {Autowiredprivate OrderMapper orderMapper;/*** 处理超时订单的方法*/Scheduled(c…

【嵌入式之RTOS】什么是消息队列

目录 一、FreeRTOS消息队列的基本概念 二、FreeRTOS消息队列的工作原理 三、FreeRTOS消息队列的特点 四、FreeRTOS消息队列的应用 五、示例 消息队列是一种用于任务间通信的机制&#xff0c;它允许一个任务&#xff08;生产者&#xff09;向消息队列发送消息&#xff0c;而…

使用uniapp+Django开发的在线工具网站

引言 在当今数字化时代&#xff0c;在线工具网站为用户提供了便捷的服务和功能&#xff0c;本文分享了我使用UniApp和Django开发的一款多平台在线工具网站。通过这个项目&#xff0c;我探索了跨平台开发与强大的后端框架结合的优势&#xff0c;实现了用户友好的界面和稳健的功…

【C++】二维数组定义方式

二维数组有四种定义方式 1、数据类型 数组名[行数 ][ 列数 ]; 2、数据类型 数组名[ 行数 ][ 列数 ]{{数据1&#xff0c;数据2}&#xff0c;{数据3&#xff0c;数据4 }}; 3、数据类型 数组名[ 行数 ][ 列数 ]{数据1&#xff0c;数据2&#xff0c;数据3&#xff…

C++ 标准库和标准模板库

参考&#xff1a; C STL 教程 | 菜鸟教程 (runoob.com) C标准库和标准模板库 - 星朝 - 博客园 (cnblogs.com) C强大的功能来源于其丰富的类库及库函数资源。C标准库的内容总共在50个标准头文件中定义。在C开发中&#xff0c;要尽可能地利用标准库完成。这样做的直接好处包括&am…

【Jenkins未授权访问漏洞 】

默认情况下 Jenkins面板中用户可以选择执行脚本界面来操作一些系统层命令&#xff0c;攻击者可通过未授权访问漏洞或者暴力破解用户密码等进入后台管理服务&#xff0c;通过脚本执行界面从而获取服务器权限。 第一步&#xff1a;使用fofa语句搜索 搜索语句&#xff1a; port&…

NICE Seminar(2022-7-17)进化约束优化(中南大学王勇教授)

​​​​​​​ ACO偏向于离散 DE和PSO偏向于连续变量 确定性替换和随机替换 存在缺陷&#xff0c;可行域可能有多个。 Pfea为可行解所占比例 目标空间中的可行域与在决策空间的可行域中的对应关系&#xff1f;&#xff1f;&#xff1f; 维度中套维度&#xff1f;&#xff1…

苹果 Safari 的隐私保护与广告追踪问题 :技术进展与挑战

隐私保护的进展与挑战 近年来&#xff0c;浏览器行业在隐私保护技术方面取得了显著进展&#xff0c;尤其是在广告追踪领域。谷歌的 Chrome 浏览器推广了隐私沙盒&#xff0c;通过将用户可能感兴趣的主题分类并推送给广告商。Mozilla Firefox 和 Meta Facebook 则推出了一种名为…

C 语言学习(5) ---- 汇编语法基础

目录 汇编语言基础x86 体系的寄存器说明Intel 和 AT&T 语法"helloworld" 汇编程序分析指令和伪指令TBD 汇编语言基础 汇编程序基本由 4 种类型的组件组成&#xff1a;指令&#xff08;instruction&#xff09;、伪指令&#xff08;directive&#xff09;、标号&…

Python在指定文件夹下创建虚拟环境

基于不同python版本和第三方包版本开发的项目&#xff0c;为了方便学习和管理python环境&#xff0c;可以在指定的文件夹里创建项目所需的虚拟环境。具体流程如下&#xff1a; (1) 以管理员身份打开Ananconda Prompt&#xff0c;查看当前虚拟环境&#xff0c;输入命令如下&…