1. 程式人生 > >多執行緒使用之主執行緒與多執行緒響應同步

多執行緒使用之主執行緒與多執行緒響應同步

需求:

匯出資料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());
				}
			}
		}
	}



多執行緒之路漫漫,還要不段摸索 

感謝觀看