四种线程池的创建及任务提交

1. 线程池概述

1.1 线程池的定义

        线程池是管理和控制线程使用的一种手段。它通过提前创建一定数量的线程,并将任务提交给这些线程执行,来实现资源的合理分配和任务的高效处理。

关键点

  • 线程复用:线程池在任务执行完毕后,不会销毁线程,而是让线程继续等待下一个任务。
  • 任务调度:线程池可以通过队列对任务进行排序和分发,确保资源合理利用。
  • 统一管理:线程池对线程的创建、销毁、调度进行统一管理,避免资源浪费。

1.2 为什么使用线程池?

使用线程池可以带来以下好处:

  1. 避免频繁创建销毁线程的开销

    • 每次创建线程需要分配资源,如内存堆栈、CPU 调度等,销毁线程也需要一定的代价。
    • 线程池通过线程复用机制,减少了这种频繁操作的系统开销。
  2. 限制并发数量,防止系统过载

    • 无限制地创建线程可能导致系统资源耗尽,甚至引发宕机。
    • 线程池通过设置最大线程数量,合理控制并发任务数。
  3. 提高任务响应速度

    • 线程池中的线程是提前创建好的,当有新任务时可以立即分配线程执行,而无需等待线程创建。
  4. 统一管理任务队列和线程生命周期

    • 线程池可以管理任务的执行顺序,提供优先级支持,并统一处理线程的生命周期。

1.3 线程池的工作原理

线程池的基本工作流程如下:

  1. 任务提交

    • 用户将任务提交给线程池,任务可以是实现了 RunnableCallable 接口的对象。
    • 线程池会将任务放入任务队列中等待执行。
  2. 线程分配

    • 如果线程池中有空闲线程,则立即分配线程执行任务。
    • 如果没有空闲线程,且线程池未达到最大线程数,则创建新线程来处理任务。
    • 如果线程数已达上限,任务会进入等待队列,直到有空闲线程可用。
  3. 任务执行

    • 被分配的线程从任务队列中取出任务,调用 run()call() 方法来执行任务逻辑。
  4. 线程回收

    • 任务执行完毕后,线程不会被销毁,而是返回线程池等待下一次任务分配。
    • 如果线程长时间空闲且超出线程池的核心线程数限制,线程会被销毁以节约资源。

工作原理图

任务提交 → 加入任务队列 → 分配线程 → 任务执行 → 线程回收

1.4 线程池的核心参数

线程池的行为主要由以下核心参数控制:

  1. 核心线程数(corePoolSize)

    • 线程池中始终保持的线程数量,即使线程处于空闲状态也不会回收。
    • 适合设置为系统平均负载的并发数量。
  2. 最大线程数(maximumPoolSize)

    • 线程池中允许创建的最大线程数量,控制资源消耗的上限。
  3. 任务队列(workQueue)

    • 用于保存待执行任务的队列类型,如 ArrayBlockingQueueLinkedBlockingQueue
  4. 线程存活时间(keepAliveTime)

    • 超过核心线程数的线程在空闲状态下能存活的时间,适用于动态调整线程池规模。
  5. 线程工厂(ThreadFactory)

    • 定制线程的创建方式,如线程命名、优先级设置等。
  6. 任务拒绝策略(RejectedExecutionHandler)

    • 当线程池及其队列都满了时,如何处理新提交的任务:
      • AbortPolicy:直接抛出异常(默认策略)。
      • CallerRunsPolicy:由提交任务的线程自己执行任务。
      • DiscardPolicy:直接丢弃任务,不抛异常。
      • DiscardOldestPolicy:丢弃队列中最老的任务。

2. 提交任务的方法

线程池的任务提交方式主要分为以下两种,分别对应不同的应用场景和返回值需求。

2.1 提交方式一:execute() 方法

execute() 是线程池的基本任务提交方法,适用于不需要任务执行结果的场景。

方法定义

void execute(Runnable command)

工作原理

  1. 将任务提交到线程池。
  2. 如果线程池中有空闲线程,立即执行任务。
  3. 如果没有空闲线程,任务将被放入任务队列。
  4. 如果队列已满且线程数已达最大值,则触发拒绝策略。

示例代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecuteExample {
    public static void main(String[] args) {
        // 创建一个固定大小为3的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // 提交任务
        for (int i = 1; i <= 5; i++) {
            int taskId = i;
            executorService.execute(() -> {
                System.out.println("任务 " + taskId + " 正在执行,线程:" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("任务 " + taskId + " 执行完毕");
            });
        }

        // 关闭线程池
        executorService.shutdown();
    }
}

输出示例

任务 1 正在执行,线程:pool-1-thread-1
任务 2 正在执行,线程:pool-1-thread-2
任务 3 正在执行,线程:pool-1-thread-3
任务 1 执行完毕
任务 4 正在执行,线程:pool-1-thread-1
任务 2 执行完毕
...

优点

  • 简单快捷。
  • 适合不需要返回值的任务。

注意

  • 无法直接获取任务的执行结果。
  • 如果任务中抛出异常,可能无法感知。

2.2 提交方式二:submit() 方法

submit() 方法更灵活,可以用来提交 RunnableCallable 任务,并返回一个 Future 对象,以便在后续获取任务执行结果。

方法定义

<T> Future<T> submit(Callable<T> task)
Future<?> submit(Runnable task)

工作原理

  1. 将任务提交到线程池。
  2. 任务执行后,结果会存储在 Future 对象中。
  3. 可以通过 Futureget() 方法获取结果,或在任务未完成时进行阻塞等待。

示例代码

import java.util.concurrent.*;

public class SubmitExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 创建一个固定大小为2的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // 提交 Runnable 任务
        Future<?> runnableFuture = executorService.submit(() -> {
            System.out.println("Runnable 任务正在执行,线程:" + Thread.currentThread().getName());
        });

        // 提交 Callable 任务
        Future<String> callableFuture = executorService.submit(() -> {
            System.out.println("Callable 任务正在执行,线程:" + Thread.currentThread().getName());
            return "任务结果";
        });

        // 等待任务完成并获取结果
        System.out.println("Callable 任务的返回值:" + callableFuture.get());

        // 关闭线程池
        executorService.shutdown();
    }
}

输出示例

Runnable 任务正在执行,线程:pool-1-thread-1
Callable 任务正在执行,线程:pool-1-thread-2
Callable 任务的返回值:任务结果

优点

  • 支持返回结果(适用于 Callable)。
  • 可以通过 Future 检测任务完成状态或取消任务。

注意

  • get() 方法会阻塞当前线程,直到任务完成。
  • 如果任务抛出异常,Future.get() 会抛出 ExecutionException

2.3 两种提交方法的对比

特性execute()submit()
任务类型仅支持 Runnable支持 RunnableCallable
返回值无返回值返回 Future,可获取结果或检查状态
任务抛出异常的处理方式可能被忽略通过 Future.get() 抛出异常
使用场景不关心任务结果的简单任务需要获取任务结果或处理异常的场景

小结

  • execute() 是基础的任务提交方式,适合无结果任务。
  • submit() 更灵活,适合需要结果或异常处理的任务。

3. 四种线程池的创建

        Java 提供了 Executors 工具类,用于快速创建常见类型的线程池。这些线程池类型根据不同的需求场景设计,可以有效管理线程的创建和回收。

3.1 创建方式一:固定大小线程池(FixedThreadPool)

特点

  • 拥有固定数量的线程。
  • 无论有多少任务,线程池中的线程数量始终保持不变。
  • 任务超出线程数量时,将进入任务队列等待执行。

适用场景

  • 稳定且长期运行的任务,线程数量可根据系统资源确定。

创建方式

ExecutorService executorService = Executors.newFixedThreadPool(int nThreads);

示例代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        // 创建固定大小为3的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // 提交5个任务
        for (int i = 1; i <= 5; i++) {
            int taskId = i;
            executorService.execute(() -> {
                System.out.println("任务 " + taskId + " 正在执行,线程:" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // 模拟任务耗时
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        executorService.shutdown();
    }
}

运行效果: 固定数量的线程执行任务,其余任务在队列中等待:

任务 1 正在执行,线程:pool-1-thread-1
任务 2 正在执行,线程:pool-1-thread-2
任务 3 正在执行,线程:pool-1-thread-3
任务 4 等待执行
任务 5 等待执行

3.2 创建方式二:单线程线程池(SingleThreadExecutor)

特点

  • 只有一个线程在执行任务。
  • 所有任务会按照提交的顺序(FIFO,先进先出)执行。
  • 适合需要顺序执行任务的场景。

适用场景

  • 确保任务按顺序执行。
  • 不需要并发操作时,例如日志记录。

创建方式

ExecutorService executorService = Executors.newSingleThreadExecutor();

示例代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        // 创建单线程线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        // 提交3个任务
        for (int i = 1; i <= 3; i++) {
            int taskId = i;
            executorService.execute(() -> {
                System.out.println("任务 " + taskId + " 正在执行,线程:" + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        executorService.shutdown();
    }
}

运行效果: 任务严格按顺序执行:

任务 1 正在执行,线程:pool-1-thread-1
任务 2 正在执行,线程:pool-1-thread-1
任务 3 正在执行,线程:pool-1-thread-1

3.3 创建方式三:缓存线程池(CachedThreadPool)

特点

  • 线程数量不固定,动态调整。
  • 如果有空闲线程可用,则复用空闲线程;如果没有空闲线程,则创建新线程。
  • 空闲线程超过 60 秒未使用会被销毁。

适用场景

  • 大量短期任务,且任务数量不确定。

创建方式

ExecutorService executorService = Executors.newCachedThreadPool();

示例代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        // 创建缓存线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        // 提交10个任务
        for (int i = 1; i <= 10; i++) {
            int taskId = i;
            executorService.execute(() -> {
                System.out.println("任务 " + taskId + " 正在执行,线程:" + Thread.currentThread().getName());
            });
        }

        // 关闭线程池
        executorService.shutdown();
    }
}

运行效果: 任务数量多时,会快速创建新线程:

任务 1 正在执行,线程:pool-1-thread-1
任务 2 正在执行,线程:pool-1-thread-2
...
任务 10 正在执行,线程:pool-1-thread-10

3.4 创建方式四:定时线程池(ScheduledThreadPool)

特点

  • 用于执行定时任务或周期性任务。
  • 可以延迟执行任务或按照固定时间间隔循环执行任务。

适用场景

  • 周期性任务调度,例如定时备份、定时发送邮件。

创建方式

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(int corePoolSize);

示例代码

import java.util.concurrent.*;

public class ScheduledThreadPoolExample {
    public static void main(String[] args) {
        // 创建定时线程池
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);

        // 延迟3秒执行任务
        scheduledExecutorService.schedule(() -> {
            System.out.println("延迟执行任务,线程:" + Thread.currentThread().getName());
        }, 3, TimeUnit.SECONDS);

        // 每2秒执行一次任务
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            System.out.println("周期性任务,线程:" + Thread.currentThread().getName());
        }, 1, 2, TimeUnit.SECONDS);
    }
}

运行效果

  • 延迟 3 秒后执行一次任务。
  • 每 2 秒执行一次周期性任务。

小结

类型特点适用场景
FixedThreadPool固定线程数量,任务超出进入队列稳定并发任务
SingleThreadExecutor单线程,顺序执行任务需要任务严格按顺序执行的场景
CachedThreadPool线程数量动态调整短期、大量任务,任务数不确定
ScheduledThreadPool支持定时和周期性任务定时任务和循环任务

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python pyside6 设置的一个《广告图片生成器》

一、图&#xff1a; 二、说明书&#xff1a; 广告图片生成器使用说明 软件功能 这是一个用于生成广告图片的工具&#xff0c;可以快速制作包含产品图片和文字的广告图片。 主要特点 自定义广告尺寸&#xff08;默认620420像素&#xff09; 智能去除产品图片背景 自动排版&…

Spark基本介绍

一&#xff0c;Spark是什么 1.定义&#xff1a;Aache Spark是用于大规模数据处理的统一分析引擎。 二&#xff0c;Spark的发展 三&#xff0c;Spark的特点 高效性 计算速度快 提供了一个全新的数据结构RDD&#xff08;弹性分布式数据集&#xff09;。整个计算操作&#xff0c;…

Elasticsearch操作笔记版

文章目录 1.ES索引库操作(CRUD)1.mapping常见属性(前提)2.创建索引库3.查询&#xff0c;删除索引库4.修改索引库 2.ES文档操作(CRUD)1.新增文档2.查询、删除文档查询返回的数据解读&#xff1a; 3.修改文档 3.RestClient操作(索引库/文档)(CRUD)1.什么是RestClient2.需要考虑前…

EFEVD: Enhanced Feature Extraction for Smart Contract Vulnerability Detection

假设&#xff0c;攻击者在合约 Dao 内存放有 1 Ether 攻击者调用 withdraw 函数&#xff0c;提取 1 Ether&#xff1b; 函数执行到 require 之后&#xff0c; balances 之前时&#xff0c;6789-6789-6789- contract Dao {function withdraw() public {require(balances[msg.…

我的线代观-秩(向量,矩阵)

都说秩是线代中不可避免的一环&#xff0c;当然&#xff0c;它其中最重要的一环。 我在学习线代之后&#xff0c;也有这种感受&#xff0c;它有着一种很绕的感受。 1.矩阵中 在矩阵中&#xff0c;它的秩是怎么定义的呢。它常常与行列式扯上关系&#xff0c;我们拿三阶矩阵为例…

ES IK分词字典热更新

前言 在使用IK分词器的时候&#xff0c;发现官方默认的分词不满足我们的需求&#xff0c;那么有没有方法可以自定义字典呢&#xff1f; 官方提供了三种方式 一、ik本地文件读取方式 k插件本来已为用户提供自定义词典扩展功能&#xff0c;只要修改配给文件即可&#xff1a; …

基于Spring Boot的电影网站系统

一、技术架构 后端框架&#xff1a;Spring Boot&#xff0c;它提供了自动配置、简化依赖管理、内嵌式容器等特性&#xff0c;使得开发者可以快速搭建起一个功能完备的Web应用。 前端技术&#xff1a;可能采用Vue.js、JS、jQuery、Ajax等技术&#xff0c;结合Element UI等组件库…

C#运动控制系统:雷赛控制卡实用完整例子 C#雷赛开发快速入门 C#雷赛运动控制系统实战例子 C#快速开发雷赛控制卡

雷赛控制技术 DMC系列运动控制卡是一款新型的 PCI/PCIe 总线运动控制卡。可以控制多个步进电机或数字式伺服电机&#xff1b;适合于多轴点位运动、插补运动、轨迹规划、手轮控制、编码器位置检测、IO 控制、位置比较、位置锁存等功能的应用。 DMC3000 系列卡的运动控制函数库功…

android studio 写一个小计时器(版本二)

as版本&#xff1a;23.3.1patch2 例程&#xff1a;timer 在前一个版本的基本上改的&#xff0c;增加了继续的功能&#xff0c;实现方法稍微不同。 动画演示&#xff1a; activity_main.xml <?xml version"1.0" encoding"utf-8"?> <androidx…

python-leetcode-轮转数组

189. 轮转数组 - 力扣&#xff08;LeetCode&#xff09; class Solution:def rotate(self, nums: List[int], k: int) -> None:"""Do not return anything, modify nums in-place instead."""n len(nums)k % n # 如果 k 大于 n&#xff0c;…

亚马逊云科技 | Amazon Nova:智能技术新势力

在2024年亚马逊云科技re:invent大会上&#xff0c;Amazon Nova 系列自研生成式 AI 多模态模型重磅登场&#xff0c;新一代的AI产品-Amazon Nova&#xff0c;隶属于 Amazon Bedrock&#xff0c;一共发布6款大模型&#xff0c;精准切入不同领域&#xff0c;解锁多元业务可能&…

记录第一次跑YOLOV8做目标检测

今天是24年的最后一天&#xff0c;终于要向新世界开始破门了&#xff0c;开始深度学习&#xff0c;YOLO来敲门~ 最近做了一些皮肤检测的功能&#xff0c;在传统的处理中经历了反复挣扎&#xff0c;终于要上YOLO了。听过、看过&#xff0c;不如上手体会过~ 1、YOLO是什么&#x…

从授权校验看SpringBoot自动装配

背景 最近需要实现一个对于系统的授权检测功能&#xff0c;即当SpringBoot应用被启动时&#xff0c;需要当前设备是否具有有效的的授权许可信息&#xff0c;若无则直接退出应用。具体的实现方案请继续看下文。 环境 Ruoyi-Vue SpringBoot3 RuoYi-Vue: &#x1f389; 基于Spr…

【Unity】 HTFramework框架(五十七)通过Tag、Layer批量搜索物体

更新日期&#xff1a;2024年12月30日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 问题再现通过Tag搜索物体&#xff08;SearchByTag&#xff09;打开SearchByTag窗口搜索标记指定Tag的所有物体批量修改Tag搜索Undefined状态的所有物体 …

Unity2D无限地图的实现(简单好抄)

说明&#xff1a;本教程实现的是在2D游戏中玩家在游戏中上下左右移动的时候自动进行地图拼接的功能&#xff0c;如果你只想实现左右移动的无限地图&#xff0c;那么这篇博客也能起到一定参考作用。 思路 第一步&#xff1a; 创建一个10*10的2D游戏对象当做地图 第二步创建一个…

艾体宝方案丨全面提升API安全:AccuKnox 接口漏洞预防与修复

一、API 安全&#xff1a;现代企业的必修课 在现代技术生态中&#xff0c;应用程序编程接口&#xff08;API&#xff09;扮演着不可或缺的角色。从数据共享到跨平台集成&#xff0c;API 成为连接企业系统与外部服务的桥梁。然而&#xff0c;伴随云计算的普及与微服务架构的流行…

日期时间选择(设置禁用状态)

目录 1.element文档需要 2.禁用所有过去的时间 3.设置指定日期的禁用时间 <template><div class"block"><span class"demonstration">起始日期时刻为 12:00:00</span><el-date-pickerv-model"value1"type"dat…

SAP学习笔记 - 豆知识14 - Msg 番号 M7562 - 取引Type WL 对应的番号範囲中不存在2025年度 OMBT

这种类似的以前也写过&#xff0c;原因就是自动採番的番号没弄。 比如跨年了&#xff0c;那该新年度的番号范围没弄啊&#xff0c;就会出这种错误。 把番号范围给加一下就可以了。 1&#xff0c;现象 比如点 VL02N 出荷传票变更 画面&#xff0c;点 出库确认 就会出如下错误…

SpringBoot 集成 Activiti 7 工作流引擎

一. 版本信息 IntelliJ IDEA 2023.3.6JDK 17Activiti 7 二. IDEA依赖插件安装 安装BPM流程图插件&#xff0c;如果IDEA的版本超过2020,则不支持actiBPM插件。我的IDEA是2023版本我装的是 Activiti BPMN visualizer 插件。 在Plugins 搜索 Activiti BPMN visualizer 安装 创…

分布式版本管理工具——Git关联远程仓库(github+gitee)

Git远程仓库&#xff08;Github&#xff09;的基本使用 一、前言二、Git远程仓库介绍三、演示1. 关联github远程仓库2. 关联gitee&#xff08;码云&#xff09;远程仓库3. 重命名远程仓库名4. 移除远程仓库 四、结束语 一、前言 古之立大事者&#xff0c;不惟有超世之才&#x…