多线程使用之主线程与多线程响应同步

来源:互联网 发布:施乐2011网络设置 编辑:程序博客网 时间:2024/06/03 19:31

需求:

导出数据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 blocke.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文件中的sheetHSSFSheet 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());}}}}



多线程之路漫漫,还要不段摸索 

感谢观看


原创粉丝点击