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

需求:

导出数据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());
				}
			}
		}
	}



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

感谢观看


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值