公司的需求,当前某个Excel导入功能,流程是:读取Excel数据,传入后台校验每一条数据,判断是否符合导入要求,返回给前端,导入预览展示。(前端等待响应,难点)。用户再点击导入按钮,进行异步导入(前端不等待,好做)。当前接口仅支持300条数据,现在要求我要支持3000条数据。

解决问题,思路是关键。

首先,查看接口,找到读取表格的位置,看到判断,如果数据量大于300,直接返回。把300改成3000.

然后,分析导入数据校验,都是和哪些数据进行校验的,这些数据都是从数据库来的。每一次都从数据库查询,那肯定是慢的。就算是查询Redis缓存,也要有网络消耗,增加缓存的压力。虽然单机Redis有12万次/秒的查询性能,12万除以3000得40,如果这样玩,40个人使用就拖垮系统了。同一个数据,非要查3000次,那是不是傻???所以减少每一次的查询,把数据库查询都加上Redis缓存,把Redis缓存查到的数据,在方法中创建并发安全容器ConcurrentHashMap存储数据,避免重复的查询操作,只查一次直到方法调用结束。

Map map = newConcurrentHashMap();

Object obj= map.get("key");if (null ==obj){//查询缓存,或者数据库

String value = "数据";

map.put("key", value);

}

方法内部创建的对象,当方法调用完成,进栈出栈,释放引用,就会释放内存。在3000次校验的过程中,Object对象,是在jvm内存中的,方便被快速的重复使用,而不是需要再次从数据库或者缓存中获取。这是方法栈级别的缓存,JVM缓存,本地缓存。

这就是最重要的思想,思维。做到一个方法中,尽量少的查询,把查询的结果重复利用。

当我做完了在方法中用ConcurrentHashMap缓存数据,就进行了测试。

结果:最多支持800条导入数据的校验。前端请求超过10秒,就会请求超时。

怎么办呢???

产品,你这个需求搞不定啊。无法实现啊。。。。。。扯皮中。。。。。扯皮无效。

接着用多线程技术进行优化。

1.创建线程池

importjava.util.concurrent.ExecutorService;importjava.util.concurrent.LinkedBlockingDeque;importjava.util.concurrent.ThreadPoolExecutor;importjava.util.concurrent.TimeUnit;/*** 线程池

*

*

*@author*@version

*/

public classMyExecutor {/*** 在池中保持的线程的最小数量*/

private static final int CorePoolSize = 10;/*** 线程池中能容纳的最大线程数量,如果超出,则使用RejectedExecutionHandler拒绝策略处理*/

private static final int MaximumPoolSize = 200;/*** 线程的最大生命周期。这里的生命周期有两个约束条件:

* 一:该参数针对的是超过corePoolSize数量的线程;

* 二:处于非运行状态的线程。举个例子:如果corePoolSize(最小线程数)为10,maxinumPoolSize(最大线程数)为20,

* 而此时线程池中有15个线程在运行,过了一段时间后,其中有3个线程处于等待状态的时间超过keepAliveTime指定的时间,

* 则结束这3个线程,此时线程池中则还有12个线程正在运行。*/

private static final int KeepAliveTime = 30;/*** 等待任务队列大小*/

private static final int Capacity = 10000;private static final ExecutorService pool = new ThreadPoolExecutor(CorePoolSize, MaximumPoolSize, KeepAliveTime, TimeUnit.SECONDS, new LinkedBlockingDeque<>(Capacity));public staticExecutorService getPool(){returnpool;

}

}

2.创建用于接收线程池任务返回值有序集合,方便依次获取结果。

List> futureList = new LinkedList<>();

//说明:ArrayList和LinkedList的大致区别:

// 1.ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。

// 2.对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。

// 3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

// 因为要add()3000次数据,所以选择LinkedList

3.获取线程池

//获取线程池

ExecutorService pool = MyExecutor.getPool();

4.读取Excel表格数据,遍历每一行,每一行数据都提交一个任务到多线程。

//提交Callable任务到线程池

Future future = pool.submit(new Callable() {

@Overridepublic Object call() throwsException {//每条数据的计算

return null;

}

});//把单个结果加入有序集合中。

futureList.add(future);

5.遍历futureList获取结果。

for (FutureoneFuture : futureList) {try{//每一个任务的结果,阻塞方法,一直等待到计算任务完成。

Object result =oneFuture.get();

}catch(Exception e) {

e.printStackTrace();

}

}

6.如此,把所有结果组合起来,返回。就完成了这个方法的线程池运用的改造。

7.这时候,又出现一个问题,3000条数据,每条数据都有一个id,如何在多线程里,让处理过的id不重复,出现重复还能做标记呢???

CopyOnWriteArraySet 和 ConcurrentSkipListSet 介绍:

CopyOnWriteArraySet 它是线程安全的无序的集合,可以将它理解成线程安全的HashSet。对其所有操作使用内部 CopyOnWriteArrayList 的 Set。因此,它共享以下相同的基本属性:

⑴它最适合于具有以下特征的应用程序:set 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。

⑵它是线程安全的。

⑶因为通常需要复制整个基础数组,所以可变操作(add、set 和 remove 等等)的开销很大。 迭代器不支持可变 remove操作。

⑷使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。

ConcurrentSkipListSet是线程安全的有序的集合,适用于高并发的场景。他是一个基于 ConcurrentSkipListMap 的可缩放并发 NavigableSet 实现。

⑴和TreeSet一样,支持自然排序,可以在构造的时候定义比较器;

⑵其中的contains, add, remove操作都是线程安全的。

⑶但对于批量操作,比如addAll removeAll, containsAll并不能保证原子性执行,因为其底层还是调用contains, add, remove方法,在批量操作时,只能保证每一个的add等操作是原子性的,对于批量操作在调用时,还是要手动加上锁保证原子性;

⑷不允许存储空元素;

所以这里我用到了适用于高并发的Set ===>  ConcurrentSkipListSet 。

//并发安全,去重复

ConcurrentSkipListSet idSet = new ConcurrentSkipListSet<>();boolean flag =idSet.add(id);if (!flag){//添加失败,说明数据重复。

}

我们来看看ConcurrentSkipListSet的add()方法的源码:

/*** Adds the specified element to this set if it is not already present.

* More formally, adds the specified element {@codee} to this set if

* the set contains no element {@codee2} such that {@codee.equals(e2)}.

* If this set already contains the element, the call leaves the set

* unchanged and returns {@codefalse}.

*

*@parame element to be added to this set

*@return{@codetrue} if this set did not already contain the

* specified element

*@throwsClassCastException if {@codee} cannot be compared

* with the elements currently in this set

*@throwsNullPointerException if the specified element is null*/

public booleanadd(E e) {return m.putIfAbsent(e, Boolean.TRUE) == null;

}

把上面的描述内容用谷歌翻译:

如果指定的元素尚不存在,则将其添加到此集合中。

更正式地说,将指定的元素{@code e}添加到此集合if

该集合不包含{@code e2}元素,以便{@code e.equals(e2)}。

如果此集合已包含该元素,则该调用将离开该集合

不变并返回{@code false}

说明我们这里的id去重的用法完全正确。

我们再来看看Future的get()方法的源码:

/*** Waits if necessary for the computation to complete, and then

* retrieves its result.

*

*@returnthe computed result

*@throwsCancellationException if the computation was cancelled

*@throwsExecutionException if the computation threw an

* exception

*@throwsInterruptedException if the current thread was interrupted

* while waiting*/V get()throws InterruptedException, ExecutionException;

翻译:Waits if necessary for the computation to complete, and then retrieves its result .

等待计算完成所需,然后取回其结果

所以,Future的get()方法是阻塞等待的。

到此,我就完成了从开始的300条数据,到800条数据10秒响应,优化到了3000条数据7秒响应。

即完成了任务,又提高了性能。

通过这一次运用了,线程池,Future,Callable 和并发安全容器类ConcurrentHashMap、ConcurrentSkipListSet 等技术,

很大的提高了我的多线程,并发编程的技术。还有方法栈级别的数据缓存,JVM缓存,这是一个思想的飞跃。

多线程导出excel高并发_用多线程优化Excel表格数据导入校验的接口相关推荐

  1. 多线程导出excel高并发_大牛带你深入java多线程与高并发:JMH与Disruptor,确定能学会?...

    前言 今天我们讲两个内容,第一个是JMH,第二个是Disruptor.这两个内容是给大家做更进一步的这种多线程和高并发的一些专业上的处理.生产环境之中我们很可能不自己定义消息队列,而是使用 Disru ...

  2. 多线程导出excel高并发_怎么理解分布式、高并发、多线程

    一.什么是分布式? 分布式更多的是一个概念,是为了解决单个物理服务器容量和性能瓶颈问题而采用的优化手段,该领域需要解决的问题极多,在不同的技术层面上,又包括:分布式文件系统.分布式缓存.分布式数据库. ...

  3. Java多线程学习处理高并发问题

    在程序的应用程序中,用户或请求的数量达到一定数量,并且无法避免并发请求.由于对接口的每次调用都必须在返回时终止,因此,如果接口的业务相对复杂,则可能会有多个用户.调用接口时,该用户将冻结. 以下内容将 ...

  4. Java——使用多线程模拟真实高并发业务并保证安全性(一)

    作者专注于Java.架构.Linux.小程序.爬虫.自动化等技术. 工作期间含泪整理出一些资料,微信搜索[javaUp],回复 [java][黑客][爬虫][小程序][面试]等关键字免费获取资料.技术 ...

  5. 什么是高并发?与多线程有什么区别?你的项目有高并发问题吗?是如何解决的?

    高并发是指较多用户同时访问服务.高并发可以由多线程实现,但是多线程不代表就是高并发. 在会计汇有个投票调查项目,是和财政部合作的,会计人员进行填写完调查结果后,将获得5个学分.通过短信向全国500万会 ...

  6. 线程基础,多线程架构,高并发,线程安全基础知识

    线程基础,多线程架构,高并发,线程安全基础知识 线程基础 一.线程安全 1.1概念:当多个线程访问某一个类(对象或者方法)时,这个类始终都能表现出正确额行为,那么这个类(对象或者方法)就是线程安全的. ...

  7. solr 高并发_精妙绝伦!阿里资深架构师撰写这份:并发编程,可谓“独具匠心”...

    写在前面 并发编程是Java程序员最重要的技能之一,也是最难掌握的一种技能.现在几乎100%的公司不但面试都必须问到并发编程,而且在日常工作和开发当中更是需要并发编程的使用,尤其是在互联网公司,它要求 ...

  8. 高并发访问服务器时前端优化

    高并发访问服务器时前端优化 高并发的痛点:数据流动过程两端失衡了,会导致前端到后台部分的请求会被后台拒掉甚至可能会击垮后台.需要把两端重新回到数据流动的平衡状态.前端层面尽可能地加强其作为用户与后台之 ...

  9. 【在线网课】Java高性能高并发秒杀系统方案优化实战

    java教程视频讲座简介: Java高性能高并发秒杀系统方案优化实战 Java秒杀系统方案优化 高性能高并发实战 以"秒杀"这一Java高性能高并发的试金石场景为例,带你通过一系列 ...

最新文章

  1. fcm算法c语言实现,基于特征权重的FCM算法研究及应用
  2. 行高 line-height
  3. 关于Spring中IOC容器和AOP协同工作的原理理解
  4. python如何运行py程序_如何用Python汇款:Web3.py教程
  5. wordpress静态文件加速,整合CDN
  6. LoadRunner9 5新特性
  7. vmplayer虚拟机中如何找到网络配置的窗口
  8. DICOM图像像素值、灰度值与CT值
  9. 1123581321递归算法java_经典算法设计方法
  10. C#(pronounced: see sharp) 与 .NET
  11. MATLAB下批量修改图片名称
  12. 【学堂上新】Addressable系统解析及实践经验
  13. 电子制造企业如何避免仓库出货异常,导致客户大量退货?
  14. 用html做简单课程表
  15. CentOS7安装教程
  16. 蓝宝石rx580gpuz参数_蓝宝石RX580 2048SP 4G D5 白金版 OC游戏测试
  17. 安装屏保软件(Linux终端演示 “黑客帝国” 字母雨界面)和Linux修改管理员密码
  18. 超赞!贪吃蛇、吃豆人和数字华容道等童年小游戏1行Python代码就能玩
  19. php --with-readline,php基于命令行的扩展Readline
  20. 人工智能之双色球预测系统

热门文章

  1. 有功功率、无功功率 和 视在功率之间的关联
  2. ExpandableListView 实现评价回复功能
  3. iPaste:剪贴板界的一股清流
  4. ListView 实现阻尼回弹效果 并去除边缘阴影
  5. android 向上滑动home,滑动Home键
  6. 外汇和股票有什么区别啊?
  7. 备战2022春招-java-day7
  8. Windows图标显示异常解决方法。桌面图标异常,开始菜单图标异常,任务栏图标异常。图标缓存位置。
  9. 轮播一页显示多少条数据
  10. CodeForces - 1292C Xenon‘s Attack on the Gangs(思维+dp)