一、线程池的优点:

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<ThreadConcurrencyVO> getCountNum() {
StopWatch stopWatch = new StopWatch("ThreadPoolTaskExecutor多线程方式执行任务");
stopWatch.start();
log.info("====================ThreadPoolTaskExecutor多线程方式执行任务开始========================"); List<ThreadConcurrencyVO> list = getListData(); stopWatch.stop();
log.info("====================ThreadPoolTaskExecutor多线程方式执行任务结束========================");
log.info(stopWatch.prettyPrint());
return list;
} //========================================业务逻辑==================================
/**
* 多线程方式
* */
private List<ThreadConcurrencyVO> getListData(){
List<ThreadConcurrencyVO> list = new ArrayList<>(); //获取一间教室下数据的线程
Integer testNumOne = 0;
Double avgGradeOne = 0.0;
log.info("线程一执行");
Callable<Map<String, QueryVO>> oneRoomData = () -> getCountData.getData(1);
Future<Map<String, QueryVO>> oneRoomFuture = threadPoolTaskExecutor.submit(oneRoomData);
//获取二间教室下数据的线程
Integer testNumTwo = 0;
Double avgGradeTwo = 0.0;
log.info("线程二执行");
Callable<Map<String, QueryVO>> twoRoomData = () -> getCountData.getData(2);
Future<Map<String, QueryVO>> twoRoomFuture = threadPoolTaskExecutor.submit(twoRoomData); try {
//获取一间教室下数据
Map<String, QueryVO> oneRoomMap = oneRoomFuture.get();
testNumOne = oneRoomMap.get("testCount").getTestNum();
avgGradeOne = oneRoomMap.get("testCount").getAvgGrade();
//获取二间教室下数据
Map<String, QueryVO> 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<String, QueryVO> getData(Integer room) {
Map<String, QueryVO> mapData = new HashMap<>(); //Atomic家族(事务的四个特性ACID,其中A就是原子性)主要是保证多线程环境下的原子性(指一个操作是不可中断的,同时成功,同时失败),相比synchronized而言更加轻量级
AtomicInteger testNum = new AtomicInteger(0);
AtomicReference<Double> 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;
}

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

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线程的返回结果详解:

在并发编程中,我们经常用到非阻塞的模型,在多线程的三种实现方式中,不管是继承Thread类还是实现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下:

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,数据却成功修改,可能导致错误。

搜索

复制

8、ThreadPoolTaskExecutor线程并发的更多相关文章

  1. Java线程并发:知识点

    Java线程并发:知识点   发布:一个对象是使它能够被当前范围之外的代码所引用: 常见形式:将对象的的引用存储到公共静态域:非私有方法中返回引用:发布内部类实例,包含引用.   逃逸:在对象尚未准备 ...

  2. Java多线程与并发库高级应用-java5线程并发库

    java5 中的线程并发库 主要在java.util.concurrent包中 还有 java.util.concurrent.atomic子包和java.util.concurrent.lock子包 ...

  3. Spring如何处理线程并发

    Spring如何处理线程并发   我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度.这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突.我 ...

  4. 线程高级应用-心得8-java5线程并发库中同步集合Collections工具类的应用及案例分析

    1.  HashSet与HashMap的联系与区别? 区别:前者是单列后者是双列,就是hashmap有键有值,hashset只有键: 联系:HashSet的底层就是HashMap,可以参考HashSe ...

  5. 线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

    1.Lock相关知识介绍 好比我同时种了几块地的麦子,然后就等待收割.收割时,则是哪块先熟了,先收割哪块. 下面举一个面试题的例子来引出Lock缓存读写锁的案例,一个load()和get()方法返回值 ...

  6. 线程高级应用-心得4-java5线程并发库介绍,及新技术案例分析

    1.  java5线程并发库新知识介绍 2.线程并发库案例分析 package com.itcast.family; import java.util.concurrent.ExecutorServi ...

  7. CoreJava_线程并发(堵塞队列):在某个目录下搜索含有某keyword的文件

    Java多线程编程是很考验一个程序猿水平的. 传统的WEB程序中.由于框架提供了太多的健壮性.并发性.可靠性的支持,所以我们都是将全部的注意力放到了业务实现上.我们不过依照业务逻辑的要求.不停的积累自 ...

  8. 14.6.6 Configuring Thread Concurrency for InnoDB 配置线程并发

    14.6.6 Configuring Thread Concurrency for InnoDB 配置线程并发 InnoDB 使用操作系统线程来处理请求(用户事务) 事务可能执行很多次在它们提交或者回 ...

  9. java--加强之 Java5的线程并发库

    转载请申明出处:http://blog.csdn.net/xmxkf/article/details/9945499 01. 传统线程技术回顾 创建线程的两种传统方式: 1.在Thread子类覆盖的r ...

  10. 面试心得随谈&线程并发的总结

    ---恢复内容开始--- 线程同步有两种实现方式: 基于用户模式实现和用内核对象实现.前者偏于轻量级,性能也更好,但是只能用于同一进程间的线程同步,后者重量级,性能消耗更大,跨进程. 研读了一下win ...

随机推荐

  1. 驱动开发:通过Async反向与内核通信

    在前几篇文章中给大家具体解释了驱动与应用层之间正向通信的一些经典案例,本章将继续学习驱动通信,不过这次我们学习的是通过运用Async异步模式实现的反向通信,反向通信机制在开发中时常被用到,例如一个杀毒 ...

  2. Netty 学习(八):新连接接入源码说明

    Netty 学习(八):新连接接入源码说明 作者: Grey 原文地址: 博客园:Netty 学习(八):新连接接入源码说明 CSDN:Netty 学习(八):新连接接入源码说明 新连接的接入分为3个 ...

  3. 关于docker-环境部署及拉取镜像创建容器的过程记录

    背景:因安全部门要求对特定几台应用主机的进行漏洞及脆弱性扫描,使用的工具需要基于docker环境,即他们提供镜像,让我们创建成容器,于是将整个环境安装及创建docker容器的过程记录于此 1.还是先得 ...

  4. Spring Boot 源码学习之转载

    这次的学习,主要转载了 波波老师的笔记,后续会自己整理一份 1.Spring-Boot源码分析-源码编译:https://dpb-bobokaoya-sm.blog.csdn.net/article/ ...

  5. 2022-08-14-esp32把玩记-③_轻轻松松显示个二维码(esp32+ssd1306显示图片)

    layout: post cid: 9 title: esp32把玩记-③ 轻轻松松显示个二维码(esp32+ssd1306显示图片) slug: 9 date: 2022/08/14 09:22:0 ...

  6. LcdTools如何编写MIPI指令(初始化代码)

    在LcdTools帮助文档中查看MIPI读写指令描述,如下图 编写LCM初始化代码就是配置LCM Driver IC寄存器值,一般只需用MipiWrite()指令写参数即可:下面介绍MipiWrite ...

  7. 浅谈--ETCD的基本概念及用法

    1. 简介 ETCD 是一个高可用的分布式键值数据库,可用于服务发现.ETCD 采用 raft 一致性算法,基于 Go 语言实现. raft是一个强一致的集群日志同步算法. ETCD使用gRPC,网络 ...

  8. Oracle数据库PLSQL编程和存储过程

    一.PLSQL编程 1.1.使用PLSQL实现 Hello world! 1 -- Created on 2022/8/22 by ADMINISTRATOR 2 declare 3 -- 这是申明变 ...

  9. 题解 UVA439 骑士的移动 Knight Moves

    前言 最近板子题刷多了-- 题意 一个 \(8\times 8\) 的棋盘,问马从起点到终点的最短步数为多少. \(\sf Solution\) 要求最短路径嘛,显然 bfs 更优. 读入 这个读入处 ...

  10. centos 7.6镜像_Centos7 配置本地yum源为iso镜像

    创建挂载路径 sudo mkdir /media/iso 挂载ISO镜像到目录 sudo mount -o loop CentOS-7-x86_64-Minimal-1810.iso /media/i ...