多线程详细介绍

news2025/2/26 9:27:51

一、分类
创建线程的四种方法:

(1)继承Thread
(2)实现Runnable
(3)实现Callable
(4)线程池

创建一个新的线程可以通过继承Thread类或者实现Runnable接口来实现,这两种方式创建的线程在运行结束后会被虚拟机销毁,进行垃圾回收,如果线程数量过多,频繁的创建和销毁线程会浪费资源,降低效率。而线程池的引入就很好解决了上述问题,线程池可以更好的创建、维护、管理线程的生命周期,做到复用,提高资源的使用效率,也避免了开发人员滥用new关键字创建线程的不规范行为。

说明:阿里开发手册中明确指出,在实际生产中,线程资源必须通过线程池提供,不允许在应用中显式的创建线程。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

二、两个常用线程池的详细介绍
ThreadPoolExecutor 是JDK线程池
ThreadPoolTaskExecutor 是Spring线程池

两者的区别:

ThreadPoolExecutor是一个java类不提供spring生命周期和参数装配。
ThreadPoolTaskExecutor实现了InitializingBean, DisposableBean ,xxaware等,具有spring特性
AsyncListenableTaskExecutor提供了监听任务方法(相当于添加一个任务监听,提交任务完成都会回调该方法)
简单理解:
1、ThreadPoolTaskExecutor使用ThreadPoolExecutor并增强,扩展了更多特性
2、ThreadPoolTaskExecutor只关注自己增强的部分,任务执行还是ThreadPoolExecutor处理。
3、前者spring自己用着爽,后者离开spring我们用ThreadPoolExecutor爽。
注意:ThreadPoolTaskExecutor 不会自动创建ThreadPoolExecutor需要手动调initialize才会创建
如果@Bean 就不需手动,会自动InitializingBean的afterPropertiesSet来调initialize
  1. ThreadPoolExecutor详细介绍
    在这里插入图片描述
    (1)线程池的执行顺序
    线程池按以下行为执行任务

当线程数小于核心线程数时,创建线程。
当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
当线程数大于等于核心线程数,且任务队列已满,若线程数小于最大线程数,创建线程。
若线程数等于最大线程数,则执行拒绝策略

(2)线程池的参数详解
corePoolSize
核心线程数,默认为1。
设置规则:
CPU密集型(CPU密集型也叫计算密集型,指的是运算较多,cpu占用高,读/写I/O(硬盘/内存)较少):corePoolSize = CPU核数 + 1
IO密集型(与cpu密集型相反,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。):corePoolSize = CPU核数 * 2

maximumPoolSize
最大线程数,默认为Integer.MAX_VALUE
一般设置为和核心线程数一样

keepAliveTime
线程空闲时间,默认为60s,一般设置为默认60s

unit
时间单位,默认为秒

workQueue
队列,当线程数目超过核心线程数时用于保存任务的队列。(BlockingQueue workQueue)此队列仅保存实现Runnable接口的任务。(因为线程池的底层BlockingQueue的泛型为Runnable)
无界队列
队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列作为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。阅读代码发现,Executors.newFixedThreadPool 采用就是 LinkedBlockingQueue,而博主踩到的就是这个坑,当QPS很高,发送数据很大,大量的任务被添加到这个无界LinkedBlockingQueue 中,导致cpu和内存飙升服务器挂掉。
当然这种队列,maximumPoolSize 的值也就无效了。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
有界队列
当使用有限的 maximumPoolSizes 时,有界队列有助于防止资源耗尽,但是可能较难调整和控制。常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。

同步移交队列
如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。

threadFactory
线程工厂,用来创建线程。
为了统一在创建线程时设置一些参数,如是否守护线程,线程一些特性等,如优先级。通过这个TreadFactory创建出来的线程能保证有相同的特性。
它是一个接口类,而且方法只有一个,就是创建一个线程。
如果没有另外说明,则在同一个ThreadGroup 中一律使用Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的NORM_PRIORITY 优先级和非守护进程状态。
通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。
如果从newThread 返回 null 时ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。

handler
拒绝策略,默认是AbortPolicy,会抛出异常。
当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务。
当线程池被调用shutdown()后,会等待线程池里的任务执行完毕再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务。
AbortPolicy 丢弃任务,抛运行时异常。
CallerRunsPolicy 由当前调用的任务线程执行任务。
DiscardPolicy 忽视,什么都不会发生。
DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务。

  1. ThreadPoolTaskExecutor详细介绍

异步线程配置

配置核心线程数

async.executor.thread.core_pool_size = 5

配置最大线程数

async.executor.thread.max_pool_size = 5

配置队列大小

async.executor.thread.queue_capacity = 99999

配置线程池中的线程的名称前缀

async.executor.thread.name.prefix = async-service-

示例
线程池配置类

@Configuration
@EnableAsync
public class ExecutorConfig {

    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;

    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        //ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);

        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);

    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if (null == threadPoolExecutor) {
            return;
        }

        logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }
}

业务逻辑处理类

@Service
public class DemoService {

    private static final Logger logger = LoggerFactory.getLogger(DemoService.class);

    @Override
    @Async("asyncServiceExecutor")
    public void executeAsync() {
        logger.info("start executeAsync");

        System.out.println("异步线程要做的事情");
        System.out.println("可以在这里执行批量插入等耗时的事情");

        logger.info("end executeAsync");
    }
}
@GetMapping("/test")
    public void test() {
        demoService.executeAsync();
    }

使用默认@Async进行异步操作时,注意需要配置默认线程池,示例

spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
spring.task.execution.thread-name-prefix=task-

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

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

相关文章

JAVA基础—面向对象

1、面向对象介绍 2、类和对象 2.1、如何定义类、得到类的对象、使用对象 2.2、类和对象的总结 2.3、定义类的补充事项—测试类与Javabean类 成员变量一般无需指定初始值&#xff0c;存在默认值。 但是局部变量必须定义初始值。 2.4、定义类的注意事项 2.4.1、驼峰模式 单词…

容器docker安装,以及paddle容器环境安装

例如 一台新的 服务器部署环境 1.环境服务器部署 第一步:查看显卡的驱动是否装好了,可以用nvidia-smi,正常显示表示已经安装 上面版本没有问题,但是下面版本驱动本本比较低,不支持11.2cuda安装,要更新驱动 docker, nvidia-docker 安装之前先确认如下三个 paddle 2.3 …

Win11 KB5019157(22000.1281)11月累积补丁推送了!

微软在最新推出的KB5019157更新补丁中修复了Microsoft Store更新等多个错误&#xff0c;但是仍然存在Direct Access问题。Win11用户安装之后即可升级至22000.1281&#xff0c;下面就来看看详细内容。 重要信息 KB5019157累积更新补丁 它解决了 Microsoft Store 的一些持续更新失…

shell中通配符的使用

shell中的通配符与正则表达式是不同的两种功能。 正则表达式一般不会在bash直接应用&#xff0c;需要使用sed、grep、awk来解释正则表达式。 通配符则可以通过bash直接解释&#xff0c;一般用作名称展开。 bash中可以使用的通配符符号包括&#xff1a;*、?、{}、[]、^ 这些…

【环境配置笔记】基于clang15搭建liunx内核代码阅读环境

环境&#xff1a; Toolchain&#xff08;yocto导出&#xff09;Linux5-15&#xff0c;arm64VS codeclangd15.0.3 1. VScode配置 在VS code / VS code server中配置以下插件&#xff1a; ClangdC/CC/C Extension PackC/C SnippetsCode RunnerCode Spell CheckercompareitDev…

拖拽页面元素+flip动画的案例

先上效果&#xff1a; 实现思路和流程&#xff1a; 基础页面布局 给每个拖动元素加上 draggable"true"ondragstart(开始拖动某个元素时)做出 对应的处理 获得操作的具体元素 给目标元素添加对应的样式 显示透明 增加虚线描边ondragover 被拖动的元素hover到目标元素…

java计算机毕业设计ssm养老管理系统-敬老院系统

项目介绍 1.登录页面要有验证码 2.在健康信息模块中有三个小模块分别是饮食信息、身体信息、医疗常识,饮食信息就是护工每天负责老人的饮食搭配包括;早中晚餐;各种菜谱、图片、注意事项等,身体信息就是老人的各项身体指标,医疗常识就是每一种病对应一种病例还有每天推送更新的关…

互联网获客经验分享(一)

现在的流量都很贵了。特别是公域的流量。在这样的情况下,我们如何从互联网上获取客户?满足我业务的需求呢。 今天为大家分享基础的方法,后续我们会不定期的分享一些互联网软件获客的经验,希望能够帮助到你。 流量的本质 不管是平台方还是用户,流量都是生命线。在这样的…

好心情心理咨询平台:独处≠孤独,独处对心理健康有多重要?

说到独处&#xff0c;有人认为那不就是自己一个人呆着嘛。 非也非也&#xff0c;独处其实不是要求远离人群&#xff0c;而是在信息与情感上与他人无交流&#xff0c;做到无社会互动&#xff0c;与人在网上聊天、打电话可都不能算是独处。 相反&#xff0c;自己一个人在图书馆…

ClickHouse Senior Course Ⅳ

序言 梳理一下ClickHouse的数据库引擎 首先了解下什么是数据库引擎(cuiyaonan2000163.com) 数据库引擎是用于存储、处理和保护数据的核心服务。利用数据库引擎可控制访问权限并快速处理事务&#xff0c;从而满足企业内大多数需要处理大量数据的应用程序的要求。 使用数据库引…

[附源码]java毕业设计健身房管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

《无障碍环境认证实施方案》发布

市场监管总局 中国残联关于印发 《无障碍环境认证实施方案》的通知 国市监认证发〔2022〕94号各省、自治区、直辖市和新疆生产建设兵团市场监管局&#xff08;厅、委&#xff09;、残联&#xff0c;各有关单位&#xff1a;现将《无障碍环境认证实施方案》印发给你们&#xff0…

【附源码】Python计算机毕业设计团结里小区疫情防控系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【Java|golang】775. 全局倒置与局部倒置

给你一个长度为 n 的整数数组 nums &#xff0c;表示由范围 [0, n - 1] 内所有整数组成的一个排列。 全局倒置 的数目等于满足下述条件不同下标对 (i, j) 的数目&#xff1a; 0 < i < j < n nums[i] > nums[j] 局部倒置 的数目等于满足下述条件的下标 i 的数目&a…

KEGG信号通路富集分析

KEGG信号通路富集分析 当我们手头有转录组测序后的差异分析数据后&#xff0c;想更进一步的去分析这下差异基因在信号通路富集情况&#xff0c;其中一种办法是使用KEGG网站进行信号通路富集分析。 1. 通过uniprot将Gene name转换为GeneID UniProt网站中的ID mapping具有强大…

关于HTTP/1.1,HTTP/2和HTTP/3的区别与联系

HTTP/1.1 每当说起http/1.1就会想起以前叫外卖的方式&#xff0c;那个时候很多店都没有专门的外卖员&#xff0c;打电话叫外卖&#xff0c;老板那就会叫人送货来&#xff0c;但是这样的方式有一个很大的问题&#xff0c;店员总是忘记放筷子。于是&#xff0c;店员送完外卖以后…

揭秘:如何用主题公园的思路做一款VR高尔夫游戏

作为一个从没玩过高尔夫球的人&#xff0c;我竟然第一次VR游戏中找到了高尔夫球的乐趣。由Mighty Coconut开发的《Walkabout Mini Golf》是这样一款VR游戏&#xff0c;它将迷你高尔夫与各种虚拟场景结合&#xff0c;高尔夫并不是游戏的全部&#xff0c;但你必须进球才能解锁一个…

【Python开发】Flask开发实战:个人博客(四)

Flask开发实战&#xff1a;个人博客&#xff08;四&#xff09;本篇博客将是《Flask开发实战&#xff1a;个人博客》的最后一篇。本篇文章将会详细介绍博客后台的编写。 为了支持管理员管理文章、分类、评论和链接&#xff0c;我们需要提供后台管理功能。通常来说&#xff0c;…

PDF转HTML转换器哪个好用?快看看这里

PDF格式是我们经常使用的文件格式&#xff0c;但是难免会需要将其转换成其他的格式。比如转为HTML&#xff0c;大家是不是对它很陌生呢。其实HTML是一种网页浏览格式&#xff0c;平时我们打开的浏览器页面就是用HTML代码设置而成的。那我来考考大家&#xff0c;你们知道如何将P…

redis缓存雪崩、缓存击穿和缓存穿透还傻傻分不清?看这篇就够了

前言 关于Redis的高频问题&#xff0c;缓存雪崩、缓存击穿和缓存穿透一定少不了&#xff0c;相信大家在面试中都被问到过类似的问题。为什么这些问题一直热门呢&#xff1f;因为我们在使用Redis缓存时&#xff0c;这些问题都是很容易遇到的。接下来我们就来看看这些问题都是怎…