-
2017-03-20 11:21:46
简介
事实上,使用Android Studio导入项目的时候,导入速度和Gradle以及jcenter的下载速度有关。一说到下载速度,就不得不说法力无边的墙了。
Gradle
这里有两个解决方法。
第一个就是你要学会搭建(fan)梯子(qiang),O(∩_∩)O哈哈~,这里我们就一笔带过咯。
第二个就是去下载其他人分享的国内下载地址咯。这里首先要感谢一些这些无私奉献的大佬们了。这里,我推荐几个下载地址:我们的CSDN下载 ,还有就是我们万能的百度了。建议第一个。使用方法
下载完你需要的Gradle版本之后,按照下面的提示操作。
假如你是Linux系统的,将你的gradle解压到你的用户目录下的.gradle/wrapper/dists/下。
如果你的Linux中的Android Studio是按照我的教程Linux安装常用软件 进行安装的,那么你的gradle目录就在/home/电脑用户名/.gradle/wrapper/dists/ 下。
假如你是Windows系统的,将你的gradle解压到你的C:/User/电脑用户名/.gradle/wrapper/dists/下即可。到这里,教程就结束了。有什么问题,可以一起探讨下,O(∩_∩)O哈哈~
jcenter
第一种方法
将根目录下build.gradle替换了
repositories { jcenter() }
改为阿里云的
repositories { maven{url 'http://maven.aliyun.com/nexus/content/groups/public/'} }
或者是:
repositories { maven { url 'https://dl.google.com/dl/android/maven2/' } }
第二种方法
repositories { jcenter() }
改为:
repositories { jcenter(){ url 'http://jcenter.bintray.com/'} }
更多相关内容 -
100万条数据导入SQL数据库仅用4秒
2013-01-29 17:18:22100万条数据导入SQL数据库仅用4秒,100万条数据导入SQL数据库仅用4秒 -
java线程池实例(利用多线程将400万数据导入到Elasticsearch)
2020-02-25 00:20:40项目中需要用到ES,在写完接口后,对ES进行测试时,需要导入100万,400万,500万数据,分三次导入。在用java代码导入时,100万数据用时40分钟。后边还有两次400万和500万的数据导入。所以利用多线程提高导入效率。 ...项目中需要用到ES,在写完接口后,对ES进行测试时,需要导入100万,400万,500万数据,分三次导入。在用java代码导入时,100万数据用时40分钟。后边还有两次400万和500万的数据导入。所以利用多线程提高导入效率。
我本地机器测试时,线程池的大小设为100时,性能最佳。
导入100万数据时单线程写法:
导入耗时:
2439796毫秒=2439.7秒=40.7分钟
下边是导入400万数据多线程写法:
class IntoESTask implements Runnable{ private ElasticsearchTemplate elasticsearchTemplate; private int index; public IntoESTask(ElasticsearchTemplate elasticsearchTemplate, int index) { super(); this.elasticsearchTemplate = elasticsearchTemplate; this.index = index; } @Override public void run() { News a = new News(); a.setTitle("结构化数据性能测试"); a.setTag("tag"); a.setPublishTime("2020-02-24T13:34:00Z"); try { elasticsearchTemplate.save(a); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"执行了第"+index+"任务"); } } @Test public void testCustomSaveBigData() throws Exception{ init2(); StopWatch stopwatch = new StopWatch(); stopwatch.start(); ExecutorService pool = Executors.newFixedThreadPool(100); for (int i = 0; i < 4000000; i++) { pool.execute(new IntoESTask(elasticsearchTemplate, i)); } pool.shutdown(); while(!pool.isTerminated()){ //等待所有任务完成 } stopwatch.stop(); System.out.println(stopwatch.getTime()/1000); }
利用多线程后,导入用时:
2933秒大约是48.9分钟。
-
MATLAB中批量导入.mat文件(每个文件多变量)
2021-04-18 14:29:09readall_mat.m二、代码如下:function data = readall_mat(path)% READALL_MAT 读取所有文件% DATA = READALL_MAT(PATH)读取路径PATH下的所有mat文件中的数据赋给data% mat文件中含有多个数据项% 输出cell格式以免各...一、新建MATLAB script(.m文件):readall_mat.m
二、代码如下:
function data = readall_mat(path)
% READALL_MAT 读取所有文件
% DATA = READALL_MAT(PATH)读取路径PATH下的所有mat文件中的数据赋给data
% mat文件中含有多个数据项
% 输出cell格式以免各数据项长度不同
% 输出data后若要使用data中的内容请使用data{index}访问
%
% 原始版本:V1.0 作者:贾郑磊 时间:2018.03.05
A = dir(fullfile(path,'*.mat'));
% 读取后A的格式为
% name -- filename
% date -- modification date
% bytes -- number of bytes allocated to the file
% isdir -- 1 if name is a directory and 0 if not
% ???--都显示为7.3702e+05标识
A = struct2cell(A);
num = size(A);
for k =0:num(2)-1
x(k+1) = A(num(1)*k+1);
end
m = 1;
for k = 1:num(2)
newpath = strcat(path,'\',x(k));
temp = load(char(newpath));
temp = struct2cell(temp);
num2 = size(temp);
for l = 1:num2(1)
data{m} = temp{l};
m = m+1;
end
end
% [EOF] readall_mat.m
三、调试过程用到的关键词:
1.dbstop in readall_mat at 35(或者手动添加):加断点
2.data = readall_mat('E\mydata');:运行script
3.dbcont:调试至下一断点
4.变量名:查看当前变量内容
四、运行结果:
将文件路径下的多.mat文件的多变量按文件顺序导入至data中
五、其他辅助操作
1.执行.m文件:在命令窗口输入函数名(参数);
2.中断执行中的.m文件:在命令窗口Ctrl+C
Matlab中调用VS编译的exe文件并传递变量 的方法
经历::在网上找了很多方法,都没有实现在matlab中调用vs的exe文件并且能够传递变量参数,一些小细节花费了自己很多时间,比喻忽略了一些空格! 网上很多的方法都是纯粹复制别人的方法,自己都没有去 ...
向Android模拟器中批量导入通讯录联系人
使用adb命令向Android模拟器中批量导入通讯录联系人的方法: 使用adb提供的命令, 可以非常方便地从PC中将通讯录批量导入android模拟器中. 首先要先准备好固定格式的vcf文件, 该文件 ...
使用mysql的source批量导入多个sql文件
需求: 有一个文件,文件里面包含100多个sql文件,想要把这些sql文件都导入到mysql中 做法: 使用 mysql 的 source 可以将文件导入到 mysql 中,但是一次只能导入一个 sq ...
Mysql批量导入多个sql文件
DB_edusuntk文件夹下有2000多个个sql备份文件,如何批量导入?首先新建一个main.sql,然后在main.sql文件里面这么写: source C:/sql/1.sql; source ...
从TXT文本文档向Sql Server中批量导入数据
下面我们通过以下的简单的SQL语句即可实现数据的批量导入,代码如下: Bulk insert id From 'G:\文档\test.txt' With ( fieldterminator=',', ...
将Matlab中的矩阵输出到txt文件
将矩阵输出到txt文件中的方法,遍寻网络,始见真经!!! fid=fopen('C:Documents and Settingscleantotal.ped','wt');%写入文件路径 matrix ...
Ant中批量调用TestNG的XML文件,并调用TestNgXlst生成漂亮的html测试报告
from:http://blog.csdn.net/bwgang/article/details/7865184 1.在Ant中设置如下:
-
一套超好用的“Excel导入导出+多线程处理导入数据+多线程事务回滚”的模板方法
2020-03-08 21:14:44Excel导入导出+多线程处理导入数据+多线程事务回滚的模板方法 二、功能演示: 1.Excel数据: 数据说明:第一条数据完整,可以成功导入;第二条数据无姓名,业务逻辑姓名不允许为空,会导出到错误Excel中;第...一、模板流程:
二、功能演示:
1.Excel数据:
数据说明:第一条数据完整,可以成功导入;第二条数据无姓名,业务逻辑姓名不允许为空,会导出到错误Excel中;第三条数据无姓名无类型,业务逻辑姓名类型不能为空,会导出错误Excel中。
2.导入页面:
选择相关Excel,点击导出测试按钮:
3.正确数据入库:
4.错误数据导出成Excel并有提示:
三、主要源代码:
1.Excel工具类:
这块主要有两个关于Excel的工具类,一个将Excel转化成List集合的工具类,它的名字叫ExcelToEntityListUtil.java;另一个是将List集合转化为Excel的工具类,它的名字叫EntityListToExcelUtil.java。它两有一个共同的特点是:都是单例类,而且我在这里采用的静态内部类的单例,这样做的好处是既可以懒加载,又保证了线程安全,效率也比较高。这种静态内部类的单例模式比较推荐大家去使用。相关代码如下:
ExcelToEntityListUtil.java:
package com.hanxiaozhang.utils; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.apache.poi.POIXMLDocument; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * 功能描述: <br> * 〈Excel导入成实体集合〉 * * @Author:hanxinghua * @Date: 2020/2/24 */ @Slf4j public class ExcelToEntityListUtil { private BeanStorage storage = new BeanStorage(); private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private ExcelToEntityListUtil(){} private static class Holder{ private static ExcelToEntityListUtil INSTANCE=new ExcelToEntityListUtil(); } public static ExcelToEntityListUtil getInstance(){ return Holder.INSTANCE; } /** * 执行Excel转EntityList * * @param entity * @param excel excel输入流 * @param titleToAttr key为excel的中文title,value为该中文title对于的entity属性名 * @param <T> * @return * @throws IOException * @throws InstantiationException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvalidFormatException */ public <T> ArrayList<T> execute(Class<?> entity, InputStream excel, Map<String, String> titleToAttr) throws IOException, InstantiationException, IllegalAccessException, IllegalArgumentException,InvalidFormatException { ArrayList<T> result = new ArrayList<T>(); Workbook book = create(excel); Sheet sheet = book.getSheetAt(0); int rowCount = sheet.getLastRowNum(); if (rowCount < 1) { return result; } //加载标题栏数据,以此和headMapping对应 Map<Integer, String> headTitle = loadHeadTitle(sheet); //循环行 for (int i = 1; i <= rowCount; i++) { Row row = sheet.getRow(i); //空行跳过 if (row == null) { continue; } int cellCount = row.getLastCellNum(); @SuppressWarnings("unchecked") T instance = (T) entity.newInstance(); int col = 0; try { //循环每行单元格 for (; col < cellCount; col++) { String cellValue = getCellValue(row.getCell(col)); if (null != cellValue) { this.setEntity(entity, instance, titleToAttr.get(headTitle.get(col)), cellValue); } } result.add(instance); } catch (Exception e) { String message="第" + (i + 1) + "行," + headTitle.get(col) + "字段,数据错误!"; log.info(message); throw new IllegalArgumentException(message); } } excel.close(); return result; } /** * 加载Excel的标题栏 * * @param sheet * @return 返回列序号和对于的标题名称Map */ private Map<Integer, String> loadHeadTitle(Sheet sheet) { Map<Integer, String> map = new HashMap<Integer, String>(); Row row = sheet.getRow(0); int cellCount = row.getLastCellNum(); for (int i = 0; i < cellCount; i++) { String value = row.getCell(i).getStringCellValue(); if (null == value) { throw new RuntimeException("Excel导入:标题栏不能为空!"); } map.put(i, value); } return map; } /** * 获取表格列的值 * * @param cell * @return */ private String getCellValue(Cell cell) { if (null == cell||"".equals(cell)) { return ""; } String value = ""; switch (cell.getCellType()) { case XSSFCell.CELL_TYPE_BLANK: //空值 value = null; break; case XSSFCell.CELL_TYPE_BOOLEAN: value = String.valueOf(cell.getBooleanCellValue()); break; case XSSFCell.CELL_TYPE_NUMERIC: // 判断当前的cell是否为Date if (DateUtil.isCellDateFormatted(cell)) { value = dateFormat.format(cell.getDateCellValue()); } else { value = String.valueOf((long) cell.getNumericCellValue()); } break; case XSSFCell.CELL_TYPE_STRING: value = cell.getStringCellValue(); break; case XSSFCell.CELL_TYPE_FORMULA: log.info("不支持带有函数的单元格!"); throw new IllegalArgumentException("不支持带有函数格式的单元格!"); default: log.info("单元格格式有误!"); throw new IllegalArgumentException("单元格格式有误!"); } return value; } /** * 封装实体值 * * @param clazz * @param instance * @param pro * @param value * @param <T> * @throws SecurityException * @throws NoSuchMethodException * @throws Exception */ private <T> void setEntity(Class<?> clazz, T instance, String pro, String value) throws SecurityException, NoSuchMethodException, Exception { String innerPro = null; String outterPro = null; if (pro.contains(".")) { String[] pros = pro.split("\\."); outterPro = pros[0]; innerPro = pros[1]; // 将成员变量的类型存储到仓库中 storage.storeClass(instance.hashCode() + outterPro, clazz.getDeclaredMethod(this.initGetMethod(outterPro), null).getReturnType()); } String getMethod = this.initGetMethod(outterPro != null ? outterPro : pro); Class<?> type = clazz.getDeclaredMethod(getMethod, null).getReturnType(); Method method = clazz.getMethod(this.initSetMethod(outterPro != null ? outterPro : pro), type); if (type == String.class) { method.invoke(instance, value); } else if (type == int.class || type == Integer.class) { method.invoke(instance, Integer.parseInt("".equals(value) ? "0" : value)); } else if (type == long.class || type == Long.class) { method.invoke(instance, Long.parseLong("".equals(value) ? "0" : value)); } else if (type == float.class || type == Float.class) { method.invoke(instance, Float.parseFloat("".equals(value) ? "0" : value)); } else if (type == double.class || type == Double.class) { method.invoke(instance, Double.parseDouble("".equals(value) ? "0" : value)); } else if (type == Date.class) { method.invoke(instance, dateFormat.parse(value)); } else if (type == boolean.class || type == Boolean.class) { method.invoke(instance, Boolean.parseBoolean("".equals(value) ? "false" : value)); } else if (type == byte.class || type == Byte.class) { method.invoke(instance, Byte.parseByte(value)); } else { // 引用类型数据 Object ins = storage.getInstance(instance.hashCode() + outterPro); this.setEntity(ins.getClass(), ins, innerPro, value); method.invoke(instance, ins); } } /** * 初始化set方法 * * @param field * @return */ private String initSetMethod(String field) { return "set" + field.substring(0, 1).toUpperCase() + field.substring(1); } /** * 初始化get方法 * * @param field * @return */ private String initGetMethod(String field) { return "get" + field.substring(0, 1).toUpperCase() + field.substring(1); } /** * 处理2003、2007兼容问题 * * @param inp * @return * @throws IOException * @throws InvalidFormatException */ private Workbook create(InputStream inp) throws IOException, InvalidFormatException { if (!inp.markSupported()) { inp = new PushbackInputStream(inp, 8); } if (POIFSFileSystem.hasPOIFSHeader(inp)) { return new HSSFWorkbook(inp); } if (POIXMLDocument.hasOOXMLHeader(inp)) { return new XSSFWorkbook(OPCPackage.open(inp)); } throw new IllegalArgumentException("当前Excel版本poi不能解析!"); } /** * 存储bean中的bean成员变量内部类 */ class BeanStorage { private Map<String, Object> instances = new HashMap<String, Object>(); public void storeClass(String key, Class<?> clazz) throws Exception { if (!instances.containsKey(key)) { instances.put(key, clazz.newInstance()); } } public Object getInstance(String key) { return instances.get(key); } } }
EntityListToExcelUtil.java:
package com.hanxiaozhang.utils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.xssf.usermodel.*; import static java.util.regex.Pattern.compile; /** * 功能描述: <br> * 〈导出Excel〉 * * @Author:hanxinghua * @Date: 2020/2/25 */ public class EntityListToExcelUtil { private StringBuffer error = new StringBuffer(0); private EntityListToExcelUtil(){} private static class Holder{ private static EntityListToExcelUtil INSTANCE=new EntityListToExcelUtil(); } public static EntityListToExcelUtil getInstance(){ return Holder.INSTANCE; } /** * 将实体类列表entityList转换成excel * 2003- 版本的excel * * @param attrToTitle 包含headMapping信息,key为属性名,value为列名<br> * @param entityList * @param excel * @return * @throws NoSuchMethodException * @throws SecurityException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException * @throws IOException */ @Deprecated private <T> boolean executeXLS(Map<String, String> attrToTitle, List<T> entityList, OutputStream excel) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException { System.out.println(excel.toString()); // 声明一个工作薄 HSSFWorkbook workbook = new HSSFWorkbook(); // 生成一个表格 HSSFSheet sheet = workbook.createSheet(); // 设置表格默认列宽度为15个字节 sheet.setDefaultColumnWidth(15); // 产生表格标题行 HSSFRow row = sheet.createRow(0); int i = 0; List<String> proList = new ArrayList<String>(); HSSFFont blueFont = workbook.createFont(); blueFont.setColor(HSSFColor.BLUE.index); for (Map.Entry<String, String> entry : attrToTitle.entrySet()) { HSSFCell cell = row.createCell(i); HSSFRichTextString text = new HSSFRichTextString(entry.getValue()); text.applyFont(blueFont); cell.setCellValue(text); proList.add(entry.getKey()); i++; } // 遍历集合数据,产生数据行 Iterator<T> it = entityList.iterator(); int index = 0; while (it.hasNext()) { index++; row = sheet.createRow(index); T t = (T) it.next(); // 利用反射,根据javabean属性的先后顺序,动态调用getXxx()方法得到属性值 for (i = 0; i < proList.size(); i++) { HSSFCell cell = row.createCell(i); String propertyName = proList.get(i); String textValue = null; try { textValue = this.getPropertyValue(t, propertyName); } catch (Exception e) { e.printStackTrace(); this.error.append("第").append(index + 1).append("行,列名:").append(attrToTitle.get(propertyName)).append(",字段:").append(propertyName).append(",数据错误,跳过!").append("<br>"); } // 利用正则表达式判断textValue是否全部由数字组成 if (textValue != null) { Pattern p = compile("^//d+(//.//d+)?$"); Matcher matcher = p.matcher(textValue); if (matcher.matches()) { // 是数字当作double处理 cell.setCellValue(Double.parseDouble(textValue)); } else { HSSFRichTextString richString = new HSSFRichTextString( textValue); cell.setCellValue(richString); } } } } workbook.write(excel); //关闭输出流 excel.close(); return true; } /** * 将实体类列表entityList转换成excel * 2007+ 版本的excel * * @param attrToTitle 包含headMapping信息,key为属性名,value为列名<br> * @param entityList * @param excel * @return * @throws NoSuchMethodException * @throws SecurityException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException * @throws IOException */ public <T> boolean executeXLSX(Map<String, String> attrToTitle, List<T> entityList, OutputStream excel) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException { // 声明一个工作薄 XSSFWorkbook workbook = new XSSFWorkbook(); // 生成一个表格 XSSFSheet sheet = workbook.createSheet("sheet1"); // 设置表格默认列宽度为15个字节 sheet.setDefaultColumnWidth(30); // 产生表格标题行 XSSFRow row = sheet.createRow(0); int i = 0; List<String> proList = new ArrayList<>(); //设置单元格格式 XSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setWrapText(true); //设置字体 XSSFFont blueFont = workbook.createFont(); blueFont.setColor(IndexedColors.BLUE.getIndex()); //设置表头 Iterator<Map.Entry<String, String>> itr = attrToTitle.entrySet().iterator(); while (itr.hasNext()){ Map.Entry<String, String> entry = itr.next(); XSSFCell cell = row.createCell(i); XSSFRichTextString text = new XSSFRichTextString(entry.getValue()); text.applyFont(blueFont); cell.setCellValue(text); proList.add(entry.getKey()); i++; } // 遍历集合数据,产生数据行 Iterator<T> it = entityList.iterator(); int index = 0; while (it.hasNext()) { index++; row = sheet.createRow(index); T t = (T) it.next(); // 利用反射,动态调用getXxx()方法得到属性值 for (i = 0; i < proList.size(); i++) { XSSFCell cell = row.createCell(i); cell.setCellStyle(cellStyle); String propertyName = proList.get(i); String textValue = null; try { textValue = this.getPropertyValue(t, propertyName); } catch (Exception e) { e.printStackTrace(); this.error.append("第").append(index + 1).append("行,列名:").append(attrToTitle.get(propertyName)).append(",字段:").append(propertyName).append(",数据错误,跳过!").append("<br>"); } // 利用正则表达式判断textValue是否全部由数字组成 if (textValue != null) { Pattern p = compile("^//d+(//.//d+)?$"); Matcher matcher = p.matcher(textValue); if (matcher.matches()) { // 是数字当作double处理 cell.setCellValue(Double.parseDouble(textValue)); } else { XSSFRichTextString richString = new XSSFRichTextString(textValue); cell.setCellValue(richString); } } } } workbook.write(excel); //关闭输出流 excel.close(); return true; } /** * 获取实体instance的propertyName属性的值 * * @param instance * @param propertyName * @return * @throws NoSuchMethodException * @throws SecurityException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException */ public <T> String getPropertyValue(T instance, String propertyName) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { String getMethodName = this.initGetMethod(propertyName); Class<?> tCls = instance.getClass(); Method getMethod = null; Object value = null; getMethod = tCls.getMethod(getMethodName, new Class[]{}); value = getMethod.invoke(instance, new Object[]{}); String returnType = getMethod.getReturnType().getName(); // 判断值的类型后进行强制类型转换 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String textValue = ""; if(value==null){ return textValue; } if ("java.util.Date".equals(returnType)) { textValue = dateFormat.format(value); } else { textValue = value.toString(); } return textValue; } /** * 返回fiel属性的getXXX方法字符串 * * @param field * @return */ private String initGetMethod(String field) { return "get" + field.substring(0, 1).toUpperCase() + field.substring(1); } /** * @return true 存在错误,false 不存在错误 */ public boolean hasError() { return error.capacity() > 0; } /** * 获得错误信息 * * @return */ public StringBuffer getError() { return error; } }
2.多线程处理+多线程事务回滚模块:
在实际的环境中,导入的Excel可能数据量很大,如果已经对代码进行批量插入、重复数据只查一次、代码逻辑等优化,还没有降低程序处理的时间,那我们就可以考虑使用多线程来处理数据。但是,多线程是一把双刃剑,使用得当则提高处理能力,降低程序运行时间;反之,会造成线程不安全,脏数据等致命问题。在单线程处理数据时,我们通常使用事务来避免脏数据,但是在多线程下事务可能就不好用了,原因是数据交给了多个线程去处理,事务只能控制一个线程内的数据,一个线程出错了,另一个线程的数据可能已经出入库了,破坏了数据的完整性。如何在多线程下使事务回滚呢?这里需要用到同步锁、等待与通知、Volatile关键字等相关知识。原理是这样:我们创建一个多线程结束标识的类(MultiThreadEndFlag.java),这个类会有线程总个数、失败线程数据、是否全部成功等属性(它们都使用Volatile关键字,保证线程安全),每个线程执行完业务操作后都会调用此类等待结束的方法(synchronized void waitForEnd(int resultFlag)),说明自己已经完成业务,waitForEnd方法会调用wait(),使线程停止后面代码的运行,放弃CUP运行时间进入等待池。如果每个线程都运行完业务逻辑,代码会调用多线程标识类的结束方法(end()),end方法会调用notifyAll(),通知所有线程继续运行,这时,每个线程会通过多线程标识类中是否全部成功属性去判断是否成功,如果不是成功,线程自己会自己抛出异常,使事务回滚。上面讲了如何使多线程事务回滚,下面说一说如何使用多线程,我这里会用到Executor相关线程池的知识,如何不太熟悉可以自己去学习一下。此外,我这套方法还支持业务判断有问题数据导出并提示错误原因的功能,该功能主要是通过Callable创建线程,可以通过Future获取返回值的知识实现的,具体代码如下:
MultiThreadEndFlag.java:
package com.hanxiaozhang.importexcel; import lombok.extern.slf4j.Slf4j; import java.util.UUID; /** * 功能描述: <br> * 〈多线程结束标志〉 * * @Author:hanxinghua * @Date: 2020/2/23 */ @Slf4j public class MultiThreadEndFlag { /** * 是否解除等待 */ private volatile boolean releaseWaitFlag = false; /** * 是否全部执行成功 */ private volatile boolean allSuccessFlag = false; /** * 线程个数 */ private volatile int threadCount = 0; /** * 失败个数 */ private volatile int failCount = 0; /** * 初始化子线程的总数 * @param count */ public MultiThreadEndFlag(int count){ threadCount = count; } public boolean allSuccessFlag() { return allSuccessFlag; } /** * 等待全部结束 * @param resultFlag */ public synchronized void waitForEnd(int resultFlag){ //统计失败的线程个数 if(resultFlag==0){ failCount++; } threadCount--; while (!releaseWaitFlag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 执行结束通知 */ public synchronized void go(){ releaseWaitFlag = true; //结果都显示成功 allSuccessFlag = (failCount == 0); notifyAll(); } /** * 等待结束 */ public void end(){ while (threadCount > 0){ waitFunc(50); } log.info("线程全部执行完毕通知"); go(); } /** * 等待 */ private void waitFunc(long millis){ try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } } }
ImportExcelTask.java:
package com.hanxiaozhang.importexcel; import java.util.List; import java.util.concurrent.Callable; /** * 〈功能描述〉<br> * 〈导入Excel任务〉 * * @author hanxinghua * @create 2020/2/23 * @since 1.0.0 */ public class ImportExcelTask<T> implements Callable<ErrorInfoEntity> { /** * 保存Excel服务 */ private SaveExcelService excelService; /** * 数据集合 */ private List<T> list; /** * 多线程数据结束标志 */ private MultiThreadEndFlag flag; /** * 构造函数 * * @param excelService * @param list * @param flag */ public ImportExcelTask(SaveExcelService<T> excelService,List<T> list,MultiThreadEndFlag flag){ this.excelService=excelService; this.list=list; this.flag=flag; } @Override public ErrorInfoEntity call() throws Exception { return excelService.batchSave(list,flag); } }
ErrorInfoEntity.java:
package com.hanxiaozhang.importexcel; import lombok.Data; import java.util.List; /** * 〈一句话功能简述〉<br> * 〈错误信息实体〉 * * @author hanxinghua * @create 2020/2/23 * @since 1.0.0 */ @Data public class ErrorInfoEntity<T> { /** * 业务上判断有错误的数据集合 */ private List<T> errorList; }
SaveExcelService.java:
package com.hanxiaozhang.importexcel; import java.util.List; /** * 〈一句话功能简述〉<br> * 〈保存ExcelService〉 * * @author hanxinghua * @create 2020/2/23 * @since 1.0.0 */ public interface SaveExcelService<T> { ErrorInfoEntity batchSave(List<T> list, MultiThreadEndFlag flag) throws Exception; }
DictSaveExcelServiceImpl.java:
package com.hanxiaozhang.importexcel; import com.hanxiaozhang.dictcode.dao.DictOneDao; import com.hanxiaozhang.dictcode.domain.DictDO; import com.hanxiaozhang.utils.StringUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * 〈一句话功能简述〉<br> * 〈数据字典数据导入〉 * * @author hanxinghua * @create 2020/2/23 * @since 1.0.0 */ @Slf4j @Service public class DictSaveExcelServiceImpl implements SaveExcelService<DictDO> { @Resource private DictOneDao dictOneDao; @Override @Transactional(rollbackFor = Exception.class) public ErrorInfoEntity batchSave(List<DictDO> list, MultiThreadEndFlag flag) throws Exception { int resultFlag = 0; try { //创建返回错误信息实体 ErrorInfoEntity errorInfoEntity = new ErrorInfoEntity(); //业务操作 List<DictDO> errorList = handleDict(list); //赋值错误数据 errorInfoEntity.setErrorList(errorList); //操作成功 resultFlag = 1; //等待其他线程完成操作 flag.waitForEnd(resultFlag); //其他线程异常手工回滚 if (resultFlag == 1 && !flag.allSuccessFlag()) { String message = "子线程未全部执行成功,对线程[" + Thread.currentThread().getName() + "]进行回滚"; log.info(message); throw new Exception(message); } return errorInfoEntity; } catch (Exception e) { log.error(e.toString()); //本身线程异常抛出异常,并且没有调用flag.waitForEnd()时触发 if (resultFlag == 0) { flag.waitForEnd(resultFlag); } throw e; } } /** * 处理相关数据 * * @param list * @return */ private List<DictDO> handleDict(List<DictDO> list) { List<DictDO> errorList=new ArrayList<>(); list.forEach(x->{ boolean flag=true; List<String> errorMsg=new ArrayList<>(); //模拟一个业务数据错误,姓名不能为空 if (StringUtil.isBlank(x.getName())) { errorMsg.add("姓名不能为空!"); flag=false; } //模拟一个业务数据错误,类型不能为空 if (StringUtil.isBlank(x.getType())){ errorMsg.add("类型不能为空!"); flag=false; } if (flag){ dictOneDao.save(x); }else { x.setRemarks(String.join("\n",errorMsg)); errorList.add(x); } }); return errorList; } }
ImportExcelExecutor.java:
package com.hanxiaozhang.importexcel; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import static java.util.concurrent.Executors.*; /** * 〈一句话功能简述〉<br> * 〈导入Excel执行器〉 * * @author hanxinghua * @create 2020/2/23 * @since 1.0.0 */ @Slf4j public class ImportExcelExecutor { private static int maxThreadCount=10; /** * 执行方法(分批创建子线程) * @param saveService 保存的服务 * @param lists 数据List * @param groupLen 分组的长度 * @return * @throws ExecutionException * @throws InterruptedException */ public static <T> List<T> execute(SaveExcelService<T> saveService, List<T> lists, int groupLen) throws ExecutionException, InterruptedException { if(lists==null || lists.size()==0){ return null; } List<T> errorList=new ArrayList<>(); //创建一个线程池,最大10个线程 ExecutorService executorService = newFixedThreadPool(maxThreadCount); //创建一个Future集合 List<Future<ErrorInfoEntity>> futures = new ArrayList<>(); //集合的元素个数 int size = lists.size(); //适配线程池数与分组长度 //Math.ceil()对小数向下“”取整” int batches = (int) Math.ceil(size * 1.0 /groupLen); //分组超长最大线程限制,则设置分组数为10,计算分组集合尺寸 if(batches>maxThreadCount){ batches = maxThreadCount; groupLen = (int) Math.ceil(size * 1.0 /batches); } log.info("总条数:[{}],批次数量:[{}],每批数据量:[{}]",size,batches,groupLen); MultiThreadEndFlag flag = new MultiThreadEndFlag(batches); int startIndex, toIndex, maxIndex = lists.size(); for(int i=0;i<batches;i++){ //开始索引位置 startIndex = i * groupLen; //截止索引位置 toIndex = startIndex + groupLen; //如果截止索引大于最大索引,截止索引等于最大索引 if(toIndex> maxIndex) { toIndex = maxIndex; } //截取数组 List<T> temp = lists.subList(startIndex,toIndex); if(temp == null || temp.size()==0){ continue; } futures.add(executorService.submit(new ImportExcelTask(saveService,temp,flag))); } flag.end(); //子线程全部等待返回(存在异常,则直接抛向主线程) for(Future<ErrorInfoEntity> future:futures){ errorList.addAll(future.get().getErrorList()); } //所有线程返回后,关闭线程池 executorService.shutdown(); return errorList; } }
3.使用方法:
使用这块也没有什么好说的,大家的使用方法都类型。这块要说一点Ajax不支持下载的功能,如果不知道原因,可以自己搜索一下。我这块会用到下载,大家可以去代码中看一下怎么处理的,相关代码如下:
Controller:
@GetMapping public String excelTest(){ return "importExcel"; } @ResponseBody @PostMapping("/importExcel") public R importExcel(@RequestParam(value = "file") MultipartFile file) { if (file == null) { return R.error(1, "文件不能为空"); } if (StringUtil.isBlank(file.getOriginalFilename()) || file.getSize() == 0) { return R.error(1, "文件不能为空"); } long startTime = System.currentTimeMillis(); log.info("Excel开始导入,logId:[{}]", startTime); //数据导入处理 R r = dictService.importExcel(file); if ("1".equals(r.get("code").toString())) { Map<String, Object> map = (Map) r.get("map"); map.put("logId",startTime); log.info("Excel导入出错,logId:[{}]", startTime); return R.error(1, map, "导入时有错误信息"); } long endTime = System.currentTimeMillis(); log.info("Excel导入成功,logId:[{}],导入Excel耗时(ms):[{}]", startTime,endTime-startTime); return r; } @ResponseBody @PostMapping("/exportExcel") public void exportExcel(@RequestParam("data") String data, HttpServletResponse response) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { //将Json字符串转Map Map<String, Object> params = JsonUtil.jsonToMapSO(data); log.info("Excel导出错误信息,logId:[{}]", params.get("logId").toString()); //response设置返回类型 setDownloadExcelResponse(response, params.get("fileName").toString()); //数据导出为excel EntityListToExcelUtil.getInstance(). executeXLSX(JsonUtil.jsonToLinkedHashMapSS(params.get("title").toString()), JsonUtil.jsonToList(params.get("errorData").toString(), DictDO.class), response.getOutputStream()); } /** * 设置下载文件响应信息 * * @param response * @param fileName */ private void setDownloadExcelResponse(HttpServletResponse response, String fileName) { try { fileName = new String(fileName.getBytes(), "ISO8859-1"); } catch (UnsupportedEncodingException e) { log.error("该文件[{}]不支持此编码转换,异常消息:[{}]",fileName,e.getMessage()); } response.setContentType("application/vnd.ms-excel;charset=UTF-8"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName); //使用Content-Disposition,一定要确保没有禁止浏览器缓存的操作 response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "No-cache"); response.setDateHeader("Expires", 0); }
Service:
@Override public R importExcel(MultipartFile file) { try { //读取Excel中数据 ArrayList<DictDO> list = ExcelToEntityListUtil.getInstance().execute(DictDO.class, file.getInputStream(), initTitleToAttr()); //多线程处理数据,并导出错误数据 List<DictDO> errorList = ImportExcelExecutor.execute(dictSaveExcelServiceImpl, list, 10); //封装错误数据 if (errorList!=null&&!errorList.isEmpty()) { Map<String, Object> map = new HashMap<String, Object>(); map.put("errorData", errorList); map.put("title", initAttrToTitle()); map.put("fileName", "有问题数据.xlsx"); return R.error(map); } } catch (IOException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvalidFormatException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } return R.ok(); } private Map<String,String> initTitleToAttr(){ Map<String, String> map = new LinkedHashMap<>(8); map.put("姓名","name"); map.put("值","value"); map.put("类型","type"); map.put("描述","description"); map.put("时间","createDate"); return map; } private Map<String,String> initAttrToTitle(){ Map<String, String> map = new LinkedHashMap<>(8); map.put("name","姓名"); map.put("value","值"); map.put("type","类型"); map.put("description","描述"); map.put("createDate","时间"); map.put("remarks","数据问题备注"); return map; }
页面:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div > <form id="importPlan" method="post" enctype="multipart/form-data" style="float: left"> <input class="form-control" id="file" name="file" type="file"> </form> <button type="button" onclick="importExcel()" style="float: left;margin-right: 10px">导入测试</button> <form action="/exportExcel" method="post" id="exportForm" style="display: none;"> <input type="text" name="data" id="data" value=""/> </form> </div> <script type="text/javascript" src="/jquery.min.js?v=2.1.4"></script> <script type="text/javascript" src="/importExcel.js"></script> </body> </html>
Js:
function importExcel() { $.ajax({ type: "POST", dataType: "json", cache: false, processData: false, contentType: false, url: "/importExcel", data: new FormData($('#importPlan')[0]), success: function (r) { if (r.code == 0) { alert("导入成功"); } else { $("#data").val(JSON.stringify(r.map)); $("#exportForm").submit(); } } }); }
四、源码地址:
springboot_demo: springboot_demo springboot-multi-thread模块
五、只能启动8个线程的问题(2021-12-14):
我使用该方法的时候,最多只能启动8个线程去处理excel。这个问题困扰我好久,一直没有解决。经过多方面分析,我的项目中使用Druid管理MySQL,Druid的线程池配置(maxActive:20)没有生效,它默认是8,所以最多只能启动8个线程。最后,我新增了DruidConfig配置类成功解救这个问题。配置类见com.hanxiaozhang.config.DruidConfig。
此外,我还实现了一种使用CyclicBarrier+AtomicBoolean,替换synchronized+wait()+notifyAll()实现多线程下事务回滚的方式,详情com.hanxiaozhang.importexcelnew。
六、关于等锁超时或锁死异常的问题(2021-12-22):
将Excel中数据写入数据库时,你可以会遇到等锁超时或锁死异常异常问题。你首先要分析一下数据入库的操作那里会存在上锁的情况。上述的提供的方法就是在多个线程中开启事务,等待每一个线程处理完数据,再提交事务,一定会对同一张表上锁。如果你处理的数据比较多,你可以适当调整一下MySQL引擎的锁等待时间,或者分析一下代码,调整代码逻辑避免等锁超时或死锁的情况,再或者调整一下数据库的隔离级别。
以MySQL8.0的InnoDB引擎为例,查询锁等待时间方法如下:
show VARIABLES like '%innodb_lock_wait_timeout%';
以MySQL8.0为例,查询数据库隔离级别方法如下:
show variables like 'transaction_isolation';
MySQL 8.0行锁观测方法,请参考如下文章:全新的MySQL 8.0行锁观测方式 - 老叶茶馆 - OSCHINA - 中文开源技术交流社区
-
oracle 导入imp 命令
2021-01-12 13:23:12最常用的 imp name/password@IP:1521/orcl[库] file="c:\123.dmp" full=y ignore=y。例:imp abc/123@192.168.1.3:1521/orcl file = "c:\123.dmp...oracle中imp命令详解Oracle的导入实用程序(Import utility)允许从数... -
Mysql数据库导入千万条数据
2020-02-25 11:07:21第一步 创建数据库 sql语句: CREATE DATABASE aaaa 第二步 创建表 sql语句: CREATE TABLE usertb( id serial, uname varchar(20) , ucreatetime datetime , ...DEFAULT CHARACTER SET=utf8 COLLATE=... -
使用多线程优化批量导入的回显功能
2017-12-21 15:41:23如果导入的数据特别多,校验规则也特别多,那么一条一条校验的时间就会很长,影响系统使用。这个时候可以使用多线程来并行校验。这里记录一下,以防忘掉。一般要导入的每一条数据的校验都是相互独立的,那么让几条... -
ClickHouse数据导入
2020-09-29 11:12:21文章目录ClickHouse数据导入下载样本数据创建数据库创建数据表数据表字段的数据类型数据表的表引擎导入数据检查数据查询数据 ClickHouse数据导入 参照ClickHouse官方教程来导入样本数据: ... 下载样本数据 ... -
pycharm导入sklearn库的问题
2019-07-27 12:16:53 请问如何解决呢? -
导入数据,时间格式的处理问题
2019-02-24 14:41:02将excel表中的数据到导入到数据中,由于两者的时间长度不一样,所以在导入之前需要处理一下 文章目录问题描述步骤1 将excel中的时间转化为时间格式2. 将excel 中的时间保存为csv文件![在这里插入图片描述]... -
导入EXCEL 时间数据为小数 问题
2017-04-07 20:36:22同事在做将EXCEL导入数据库功能时发现一个奇怪的问题:在EXCEL中,有一列数据明明呈现出时间格式,比如:18:35,但导到数据库中,居然一串长长的小数:0.7743055555555556,我靠,这是什么鬼。后来发现,原来EXCEL... -
关于“课前导入”你必须知道
2020-12-23 12:53:12一、导入技能的定义导入技能是教师在一个新的教学内容或教学活动开始时,应用建立问题情景的教学方式,引起学生注意、激发学生兴趣,明确学习目标,形成学习动机的一类教学行为。在其定义里我们可以看到,通过什么来... -
Springboot excel导入 EasyPOI 双表头的excel 导入数据
2020-02-24 15:53:43但是有些时间Excel的表头却不那么友好的。直接看图 这里就出现双表头的头的导入 第一步 :导包 maven <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</ar..... -
navicat如何导入大量数据?navicat要怎么才能导入大量数据啊
2020-12-18 23:11:47首先你需要先创建好一张表,双击打开例如下图然后你需要右击刚才新建的表名,选择设计表,然后将id的类型改为int,并选中左下角的自动增加然后你需要将你要导入的大量数据放入到一个Excel表中,但需要注意的是表头... -
解决数据导入错误
2020-12-22 13:20:31解决数据导入错误3 分钟将数据导入 Power BI 时,可能会遇到如下因素导致的错误:Power BI 从多个数据源导入。每个数据源可能有几十个(有时几百个)不同的错误消息。其他组件(例如硬盘驱动器、网络、软件服务和操作... -
cookie号导入后没反应,导入不全,导入浏览器失败,登录不了需要重新输入密码的原因详解
2021-02-26 19:21:29亚马逊cookie号(CK号)导入浏览器失败的原因及需要哪些软件可以成功免密登录 我们在接触亚马逊测评自养买家号、facebook账号等项目的时候一定知道什么是cookie号(ck号),如果你是新手可以去网上搜素一下,这里就... -
postgres 导入纯文本数据txt
2019-08-26 16:43:17今天碰到一个需求,大量的数据需要导入Postgres,作为一个小白,记录一下操作过程,以做记录 背景1: 使用Postgres作为存储 导入的文件:count.txt 问题1:装一个postgreSQL 用什么版本? 这个问题是第一个问题... -
Doris之导入总览
2022-01-30 14:18:00Doris之导入总览 -
python中模块是否需要单独导入(import)子包的问题
2019-04-28 17:30:28在测试jieba模块的子模块posseg时,遇到不单独导入,而是直接通过父包调用时,会发生引用不了的情况,经查原因如下: 2) 另找到这篇关于import的文章,以后备查: 《关于import,你应该知道这些内容》 ... -
为什么用pr导入视频显示文件导入失败?
2020-12-23 12:49:38导入批处理物料时,计算机被卡住,并且没有响应. 分别导入一种材料,它会提示不支持该格式并且无法导入该格式....解决方案: 分段导入材料多次,一次不要导入太多材料.如果无法解决问题,请尝试以下方法... -
excel导入mysql数据时,小数位很多时(几十位),使用的方法
2019-04-08 17:46:55把要导入的数据用=Text(数据所在单元格,“0.0000000000000”)函数转换成文本格式即可。 -
如何估算要写多少条测试用例,耗费多长时间
2021-11-06 20:57:45作为测试工程师,对测试用例的数量预估是一项基本能力,并且也很重要。因为在衡量和预报工作计划中十分重要,例如,设计用例需要几天,设计多少条、执行多长时间。 -
navicat导入excel 日期格式处理
2021-05-10 11:57:21初始化数据时,需要向数据库导入一些数据,遇到这个问题,在网上查了半天,没找到一个系统的解决办法,特此记录。 excel中原始数据是这样子的 关注三个字段:格式分别为文本、日期、日期 相应的数据库中数据... -
javaWeb项目 Excel导入速度优化
2019-02-22 10:34:09现要求将 50000+ 的数据量导入时间控制在 10m 之内。 使用 10000条数据进行测试,结果如下: Excel文件读取用时 数据检查用时 数据库写入用时 3.2m 1.3m 1.2m 打开Excel导入代码,我们可以看到是调用了jxl... -
C#,Asp.NET 导入Excel,时间格式变成数字串处理方法
2019-04-26 11:31:32在Excel中.日期或者时间格式为:42093.6506944444 或者 0....在C# 导入读取这列时,转换会发生错误; 现在将这格式转换为正常的日期格式:如下: /// <summary> /// 数字转换时间格式 /// </summary&g... -
导入maven项目很慢的问题解决方案
2020-03-22 23:52:19在导入maven项目时,会加载你本地maven库中不存在的jar包,这时会从网络中下载,然而默认是在国外的官网上下载,速度很慢,因此需要修改配置文件,添加镜像,映射到中国的服务器,速度会快很多 在apache-maven-3.6.0... -
Excel数据导入速度太慢
2018-04-11 21:54:45问题描述:某个单价设置excel导入功能,时间很长,才400条数据,并且最后报错:该记录已存在。问题分析:① 时间很长:代码里每导入一条数据,就会根据品名编码取一下产品信息,用来校验产品大类,这样频繁访问... -
IDEA下maven导入依赖很慢解决办法
2020-11-02 14:22:47IDEA下maven导入依赖很慢解决办法 ,最近在重新安装IDEA以后,无论是创建还是导入项目,pom.xml引入依赖的时候下载对应的包的速度都是很慢的,这是因为默认使用国外的镜像,需要手动配置Maven的settings.xml修改为... -
EPLAN导入edz文件太慢如何解决
2021-03-18 15:25:04目前各个品牌都在提供 EPLAN EDZ部件库文件,但是一般都是一个总的EDZ文件,导入过程中,因为电脑配置和其他问题,导致导入过程中EPLAN会崩溃或者长时间不动。我们分析下EDZ文件的构成,这是个压缩文件,换了个壳...