需求:
导出数据10000条数据到excel表中。希望用多线程优化导出速度。
设计:
将10000条数据分成10份。用10个线程分别给excel写值分10个sheet页问题:
导出数据时,由于写值启动了多线程,导出数据为空excel表格问题的梳理:
由于启动了多线程,多线程的意义是不影响主线程的响应速度,这样导致的问题是response响应给excel表了,但是值还没有写入,产生了空表解决方法:
当所有多线程执行完毕之后主线程再响应前台
注意事项:此方法不可以使用可缓存线程池去创建的多线程。这里使用的可设置数量的多线程池
代码controller 这个类是提前获取到输出流,当方法执行完毕时自动响应前台下载的文件
@RequestMapping("/common/cuijiDownLoad")
public void cuijiDownLoad(CaseHead caseHead, CuijiDownLoadEx cuijiDownLoadEx, HttpServletRequest request,
HttpServletResponse response, HttpSession session, CaseHead head, CaseParamsExtend exParams,
@RequestParam(value = "type", defaultValue = "-1") Integer type) {
response.setCharacterEncoding("UTF-8");// 设置相应内容的编码格式
// 设置导出文件文件名
String name = "案件催记";
name = name + fmt.format(new Date());
OutputStream os = null;
String companycode = SessionUtil.getCompnayCodeFromSessionByCuishouAdminOrUser(session);
try {
String fname = name + ".xls";
fname = CompanyadminController.toUtf8String(request, fname);
response.reset();// 清空输出流
response.setHeader("Content-Disposition", "attachment;filename=" + fname);
response.setContentType("application/msexcel");// 定义输出类型
os = response.getOutputStream();// 取得输出流
commonService.toExcelCuijiByCaseHead(os, caseHead, cuijiDownLoadEx);//方法
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
这里放入自定义等分list的方法
/**
* 将一个list均分成n个list,主要通过偏移量来实现的
* @param maps
* @return
*/
public static List<List<Map<String, Object>>> averageAssign(List<Map<String, Object>> maps,int n){
List<List<Map<String, Object>>> result=new ArrayList<List<Map<String, Object>>>();
int remaider=maps.size()%n; //(先计算出余数)
int number=maps.size()/n; //然后是商
int offset=0;//偏移量
for(int i=0;i<n;i++){
List<Map<String, Object>> value=null;
if(remaider>0){
value=maps.subList(i*number+offset, (i+1)*number+offset+1);
remaider--;
offset++;
}else{
value=maps.subList(i*number+offset, (i+1)*number+offset);
}
result.add(value);
}
return result;
}
判断线程完全执行完的代码
exec.shutdown();
while(true){
if(exec.isTerminated()){
writer.write("---END---\n");
writer.close();
System.out.println("所有的子线程都结束了!");
break;
}
Thread.sleep(1000);
}
exe.shutdown();该方法在加入线程队列的线程执行完之前不会执行。exe.isTerminated();当shutdown()或者shutdownNow()执行了之后才会执行,并返回true。
在上面的代码中必须有exe.isTerminated()的判断,否则在投入5个线程到线程池后会直接打印:“结束了”。不能达到我们想要的效果。
通过while(true)循环判断exe.isTerminated()重生之大文豪的值,为了防止过多的判断浪费资源,可设置线程睡眠Thread.sleep(200);
正是由于这个睡眠,所以当所有线程池中的线程都执行完后,有可能延迟200ms才执行"结束了"语句。这个参数越小延迟越小,结果越准确。
具体使用场景:
public void subMapListAndDownload(final OutputStream os,final String[] fieldHeadName, final String[] dataName,final List<Map<String, Object>> maps){
//将listmap分份
List<List<Map<String, Object>>> maplist=new ArrayList<List<Map<String, Object>>>();
if (maps.size() <1000){
//不分份
maplist.add(maps);
}else if (maps.size() >=1000 && maps.size() <2000 ){
//两等分
maplist= averageAssign(maps,2);
}else if (maps.size() >=2000 && maps.size() <3000 ){
//三等分
maplist= averageAssign(maps,3);
}else if (maps.size() >=3000 && maps.size() <4000 ){
//四等分
maplist= averageAssign(maps,4);
}else if (maps.size() >=4000 && maps.size() <5000 ){
//五等分
maplist= averageAssign(maps,5);
}else if (maps.size() >=5000 && maps.size() <6000 ){
//六等分
maplist= averageAssign(maps,6);
}else if (maps.size() >=6000 && maps.size() <7000 ){
//七等分
maplist= averageAssign(maps,7);
}else if (maps.size() >=7000 && maps.size() <8000 ){
//八等分
maplist= averageAssign(maps,8);
}else if (maps.size() >=8000 && maps.size() <9000 ){
//九等分
maplist= averageAssign(maps,9);
}else if (maps.size() >=10000 ){
//十等分
maplist= averageAssign(maps,10);
}
// 创建一个webbook,对应一个Excel文件
final HSSFWorkbook wb = new HSSFWorkbook();
//将分完份的按份数设置线程
ExecutorService cachedThreadPool = Executors.newFixedThreadPool(maplist.size()); //固定数量的线程池
for (int i = 0; i <maplist.size() ; i++) {
final List<Map<String, Object>> maps1=maplist.get(i);
//ExcelFile.writeExcelIncludeData(os, fieldHeadName, dataName, maps1);
cachedThreadPool.execute(new Runnable() {
public void run() {
if (os != null && fieldHeadName != null && dataName != null && maps1 != null)
ExcelFile.writeExcelIncludeDataSheet(os, fieldHeadName, dataName, maps1,wb);
}
});
}
boolean over=false;
cachedThreadPool.shutdown();
while (true) {
if (cachedThreadPool.isTerminated()) {
System.out.println("结束了!");
over=true;
if (over != true){
try {
Thread.sleep (3000) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
try {
wb.write(os);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
给excel添加sheet和值的方法
/**
* 导出表头和数据
*
* @param oupOutputStream
* @param fieldName
* @param mapList
*/
public static void writeExcelIncludeDataSheet(OutputStream oupOutputStream, String[] fieldName,String[] dataName,
List<Map<String, Object>> mapList, HSSFWorkbook wb ) {
// 在webbook中添加一个sheet,对应Excel文件中的sheet
HSSFSheet s = wb.createSheet();
createTagAndData(fieldName,dataName, s,mapList);// 写表格的头部
// 创建单元格,并设置值表头 设置表头居中
HSSFCellStyle style = wb.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 创建一个居中格
}
private static void createTagAndData(String[] tags,String[] dataName,HSSFSheet s,List<Map<String, Object>> mapList){
HSSFRow row = s.createRow(0);
HSSFCell cell = null;
for (int i = 0; i < tags.length; i++) {
cell = row.createCell(i);
cell.setCellValue(tags[i]);
}
for(int i=1;i<=mapList.size();i++){
row = s.createRow(i);
cell = null;
for (int s1 = 0; s1 < tags.length; s1++) {
cell = row.createCell(s1);
if(mapList.get(i-1).get(dataName[s1])!=null){
cell.setCellValue(mapList.get(i-1).get(dataName[s1]).toString());
}
}
}
}
多线程之路漫漫,还要不段摸索
感谢观看