8、ThreadPoolTaskExecutor线程并发

一、线程池的优点:

1、降低资源消耗。通过重复利用自己创建的线程降低线程创建和销毁造成的消耗。

2、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

3、提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗资源,还会降低系统的稳定性,使用线程池可以进行统一分配,调优和监控。

二、ThreadPoolTaskExecutor实现线程并发:

相关参考

ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装处理。

1、声明ThreadPoolTaskExecutor线程池配置:

@Configuration
public class TaskPoolConfig {
    /**
     * ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。
     * ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装处理。
     *
     * 拒绝策略:
     * (1)、CallerRunsPolicy: 当触发拒绝策略,只要线程池没有关闭的话,则使用调用 线程直接运行任务。一般并发比较小,性能要求不高,不允许失败。
     *     但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大
     * (2)、AbortPolicy: 丢弃任务,并抛出拒绝执行
     * (3)、RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
     * (4)、DiscardPolicy: 直接丢弃,其他啥都没有
     * (5)、DiscardOldestPolicy: 当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入
     */
    @Value("${async.thread.concurrency.coreSize:10}")
    private int coreSize;

    @Value("${async.thread.concurrency.maxSize:20}")
    private int maxSize;

    @Value("${async.thread.concurrency.queueCapacity:1000}")
    private int queueCapacity;

    @Value("${async.thread.concurrency.keepAliveSeconds:10000}")
    private int keepAliveSeconds;

    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(coreSize); //核心线程数
        executor.setMaxPoolSize(maxSize);   //最大线程数
        executor.setQueueCapacity(queueCapacity);   //最大等待队列数
        executor.setKeepAliveSeconds(keepAliveSeconds); //除核心线程,其他线程的保留时间
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());    //等待队列满后的拒绝策略
        executor.initialize();  //执行初始化
        executor.setThreadNamePrefix("async-executor-");    //线程前缀名称
        return executor;
    }
}

2、业务类:

(1)、

@Service
@Slf4j
public class ThreadConcurrencyServiceImpl implements ThreadConcurrencyService {
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;  //线程池配置声明

    @Autowired
    private GetCountData getCountData;

    @Override
    public List getCountNum() {
        StopWatch stopWatch = new StopWatch("ThreadPoolTaskExecutor多线程方式执行任务");
        stopWatch.start();
        log.info("====================ThreadPoolTaskExecutor多线程方式执行任务开始========================");

        List list = getListData();

        stopWatch.stop();
        log.info("====================ThreadPoolTaskExecutor多线程方式执行任务结束========================");
        log.info(stopWatch.prettyPrint());
        return list;
    }

//========================================业务逻辑==================================
    /**
     * 多线程方式
     * */
    private List getListData(){
        List list = new ArrayList<>();

        //获取一间教室下数据的线程
        Integer testNumOne = 0;
        Double avgGradeOne = 0.0;
        log.info("线程一执行");
        Callable> oneRoomData = () -> getCountData.getData(1);
        Future> oneRoomFuture = threadPoolTaskExecutor.submit(oneRoomData);

        //获取二间教室下数据的线程
        Integer testNumTwo = 0;
        Double avgGradeTwo = 0.0;
        log.info("线程二执行");
        Callable> twoRoomData = () -> getCountData.getData(2);
        Future> twoRoomFuture = threadPoolTaskExecutor.submit(twoRoomData);

        try {
            //获取一间教室下数据
            Map oneRoomMap = oneRoomFuture.get();
            testNumOne = oneRoomMap.get("testCount").getTestNum();
            avgGradeOne = oneRoomMap.get("testCount").getAvgGrade();
            //获取二间教室下数据
            Map twoRoomMap = twoRoomFuture.get();
            testNumTwo = twoRoomMap.get("testCount").getTestNum();
            avgGradeTwo = twoRoomMap.get("testCount").getAvgGrade();

            //List集合返回数据
            ThreadConcurrencyVO threadConcurrencyVO1 = ThreadConcurrencyVO.builder()
                    .uuid(UUID.randomUUID().toString())
                    .name("ONE")
                    .testNum(testNumOne)
                    .avgGrade(avgGradeOne)
                    .build();
            ThreadConcurrencyVO threadConcurrencyVO2 = ThreadConcurrencyVO.builder()
                    .uuid(UUID.randomUUID().toString())
                    .name("TWO")
                    .testNum(testNumTwo)
                    .avgGrade(avgGradeTwo)
                    .build();
            list.add(threadConcurrencyVO1);
            list.add(threadConcurrencyVO2);

        } catch (InterruptedException e) {
            log.info("线程中断异常:{}", e.getMessage());
        } catch (ExecutionException e) {
            log.info("线程执行异常:{}", e.getMessage());
        }
        log.info("返回结果:"+list);
        return list;
    }
}

(2)、

@Override
    public Map getData(Integer room) {
        Map mapData = new HashMap<>();

        //Atomic家族(事务的四个特性ACID,其中A就是原子性)主要是保证多线程环境下的原子性(指一个操作是不可中断的,同时成功,同时失败),相比synchronized而言更加轻量级
        AtomicInteger testNum = new AtomicInteger(0);
        AtomicReference avgGrade = new AtomicReference<>(0.0);

        Integer num = 0;
        double avg = 0.0;
        for (int i = 0; i < room * 30; i++) {
            num++;
        }
        avg = num / (num + 1000.0);
        //统计测试人数
        testNum.set(num);
        //计算占比值
        avgGrade.set(avg);

        QueryVO queryVO = QueryVO.builder()
                .testNum(testNum.get())
                .avgGrade(avgGrade.get())
                .build();
        mapData.put("testCount", queryVO);

        return mapData;
    }

三、线程池底层工作原理:

8、ThreadPoolTaskExecutor线程并发

1、在创建了线程池后,等待提交过来的任务请求。

2、当调用execute()方法添加一个请求任务时,线程池会做如下判断:

(1)、如果正在运行的线程数量小于corePoolSize(线程池的核心线程数),那么马上创建线程运行这个任务;

(2)、如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入workQueue阻塞队列中;

(3)、如果这时候workQueue阻塞队列饱和且正在运行的线程数量还小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

(4)、如果workQueue 阻塞队列满了且正在运行的线程数量大于或等于 maximumPoolSize(即:workQueue.size() + maximumPoolSize),那么线程池会启动饱和拒绝策略来执行。

3、当一个线程完成任务时,它会从workQueue 阻塞队列中取下一个任务来执行。

4、当一个线程无事可做,超过一定的时间(keepAliveTime) 时,线程池会判断:

如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

四、Thread类、Runnable接口与Callable接口的区别:

1、实现 Runnable 接口相比继承 Thread 类的优势:

(1)、可以避免由于 Java 的单继承特性而带来的局限

(2)、增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的

(3)、线程池只能放入实现 Runable 或 Callable 类线程,不能直接放入继承 Thread 的类

2、实现 Runnable 接口和实现 Callable 接口的区别:

(1)、Runnable 是自从 java1.1 就有了,而 Callable 是 1.5 之后才加上去的

(2)、实现 Callable 接口的任务线程能返回执行结果,而实现 Runnable 接口的任务线程不能返回结果

(3)、Callable 接口的 call()方法允许抛出异常,而 Runnable 接口的 run()方法的异常只能在内部消化,不能继续上抛

(4)、加入线程池运行,Runnable 使用 ExecutorService 的 execute 方法,Callable 使用 submit 方法

注:Callable 接口支持返回执行结果,此时需要调用 FutureTask.get()方法实现,此方法会阻塞主线程直到获取返回结果,当不调用此方法时,主线程不会阻塞

五、Future接口与FutureTask类获取Callable线程的返回结果详解:

在并发编程中,我们经常用到非阻塞的模型,在多线程的三种实现方式中,不管是继承 T hr ead类还是实现 Runnable接口,都 无法保证获取到之前的执行结果。而使用 Callable接口创建线程,需要实现call()方法,call()在完成时返回结果必须存储在主线程已知的对象中,可以 使用Future接口或FutureTask类获取该线程的返回结果

源码:get()用于获取任务的结果

public Object get()throws InterruptedException,ExecutionException;

1、Future对象可以在后台完成主线程中比较耗时的操作,但不会导致主线程阻塞,当主线程将来需要其执行结果时,可通过Future对象获得后台作业的计算结果或者执行状态。

2、FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果

3、Future对象或FutureTask对象调用get()方法获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,一旦计算完成,就不能再重新开始或取消计算,只会返回结果或者抛出异常。

六、线程池中submit()和execute()方法的区别:

1、execute():只能执行Runnable 类型的任务。

2、submit():可以执行Runnable和Callable类型的任务。

Callable类型的任务可以 获取执行的返回值,而 Runnable执行 无返回值。

七、多线程之Atomic原子类:

1、Atomic原子类:

Atomic (事务的四个特性ACID,其中A就是原子性) 指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。所以,所谓原子类说简单点就是具有原子/原子操作特征的类。

原子类存放在 java.util.concurrent.atomic下:

8、ThreadPoolTaskExecutor线程并发

2、实现原理:

Atomic类主要利用CAS (Compare And Swap)算法、volatile变量与native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升。

八、多线程中的CAS算法:

1、CAS算法的理解:

(1)、CAS(Compare And Swap),即比较再替换。JDK5之前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是悲观锁。JDK5增加了并发包java.util.concurrent.*,该类下采用CAS算法实现,CAS算法是一种区别于synchronouse同步锁的乐观锁。

(2)、CAS操作包含三个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,才将内存值V修改为B并返回true,否则什么都不做并返回false(需要volatile变量配合)

2、CAS算法存在的问题:

(1)、CPU开销较大

(2)、不能保证代码块的原子性

(3)、ABA问题(最大问题)

ABA问题,即并发环境下,并发1在修改数据时,虽然还是A,但已经不是初始条件的A了,中间发生了A变B,B又变A的变化,此A已经非彼A,数据却成功修改,可能导致错误。

搜索

复制

Original: https://www.cnblogs.com/Iven-L/p/16591016.html
Author: 爱文(Iven)
Title: 8、ThreadPoolTaskExecutor线程并发

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/575693/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Java基础六—Java集合框架Map

    HashMap HashMap使用hash数组+单链表实现,数组中的每个元素都是链表,由Node内部类实现,当链表长度超过8时,转化为红黑树。 HashMap实现了Map接口,即允…

    数据库 2023年6月6日
    079
  • Redis学习(1)—Redis概述

    什么是NoSQL NoSQL:Not Only SQL,意思不仅仅是SQL,它是属于 非关系型数据库。那什么是关系型数据库?数据结构是一种有行有列的数据库。 NoSQL数据库是为了…

    数据库 2023年6月14日
    082
  • 3 访问修饰符public,private,protected以及不写(默认)时的区别

    private 私有的,只对本类公开。 default 类的成员不写访问修饰符时默认为default,默认对于同一个包中的其它类相当于公开(public),对于不是同一个包中的其它…

    数据库 2023年6月6日
    098
  • 1_Layui

    官网:https://www.layui.com/ 在官网首页, 可以很方便的下载Layui Layui是一款经典模块化前端UI框架, 我们只需要定义简单的HTML,CSS,JS即…

    数据库 2023年6月11日
    095
  • Minio的安装与使用

    Minio的安装与使用 一、Minio介绍 MinIO 是在 Apache License v2.0 下发布的高性能对象存储. 就是说是个存东西的玩意,比较方便配好启动就能访问,也…

    数据库 2023年6月6日
    0109
  • zabbix监控配置流程

    1.0 zabbix监控配置流程详细 管理角度: 开发 由开发人员提供监控指标来监控 运营 让其找开发要监控指标 运维 直接加 配置角度: 创建主机 创建主机组并加入主机 添加监控…

    数据库 2023年6月14日
    0101
  • HackerRank第一趴–Basic Select

    ID number NAME VARCHAR2(17) COUNTRYCODE VARCHAR2(3) DISTRICT VARCHAR2(20) POPULATION numbe…

    数据库 2023年5月24日
    058
  • datatable 转化成xml以及json

    datatable dt=xxx获取 赋值给应用的字段 var pp=dt.row[0][“datatable里面的字段”].tostring() var …

    数据库 2023年6月9日
    085
  • 数据科学手把手:碳中和下的二氧化碳排放分析 ⛵

    💡 作者:韩信子@ShowMeAI📘 数据分析实战系列:https://www.showmeai.tech/tutorials/40📘 本文地址:https://www.showm…

    数据库 2023年6月14日
    0113
  • 多商户商城系统功能拆解30讲-平台端营销-商家优惠券

    多商户商城系统,也称为B2B2C(BBC)平台电商模式多商家商城系统。可以快速帮助企业搭建类似拼多多/京东/天猫/淘宝的综合商城。 多商户商城系统支持商家入驻加盟,同时满足平台自营…

    数据库 2023年6月14日
    099
  • 上传代码到GitHub仓库

    上传代码到GitHub仓库 准备工作 意思是自从 21 年 8 月 13 后不再支持用户名密码的方式验证了,需要创建个人访问令牌(personal access token)。 这…

    数据库 2023年6月14日
    083
  • Spring Boot之WebSocket

    1、项目地址:https://github.com/hqzmss/test01-springboot-websocket.git 2、IDE:IntelliJ IDEA 2018….

    数据库 2023年6月9日
    089
  • SQL优化这5个极简法则,直接让查询原地起飞!

    SQL 作为关系型数据库的标准语言,是 IT 从业人员必不可少的技能之一。SQL 本身并不难学,编写查询语句也很容易,但是想要编写出能够高效运行的查询语句却有一定的难度。 查询优化…

    数据库 2023年5月24日
    082
  • PostgreSQL 和 MySQL 在用途、好处、特性和特点上的异同

    PostgreSQL 和 MySQL 在用途、好处、特性和特点上的异同。 PostgreSQL 和 MySQL 是将数据组织成表的关系数据库。这些表可以根据每个表共有的数据链接或关…

    数据库 2023年5月24日
    0108
  • 如何优化前端性能?

    导读:随着前端的范畴逐渐扩大,深度逐渐下沉,富前端必然带来的一个问题就是性能。特别是在大型复杂项目中,重前端业务可能因为一个小小的数据依赖,导致整个页面卡顿甚至崩溃。本文基于Qui…

    数据库 2023年6月14日
    076
  • 微服务架构项目浅析

    微服务架构的演变 最初的需求 业务发展后需要克服的问题 微服务架构使用的组件 Nginx Redis Rabbitmq Mysql jar jdk * 总结 ​ 这个章节主要介绍微…

    数据库 2023年6月6日
    084
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球