多執行緒之futureTask(future,callable)例項,jdbc資料多執行緒查詢
阿新 • • 發佈:2019-01-01
最近遇到一個這樣的功能要求。在查詢資料時,由於查詢的資料量比較大,一次查詢(一條SQL語句中包含太多的條件)查詢起來很慢,大約要10S左右才能查詢出來,這樣體驗太不好了,需要進行優化。今天想了想,打算採用在後端把一條SQL進行拆分,拆分成為多條SQL語句,再拋給多個執行緒去分別查詢,查詢完畢後,再進行彙總的這個思路來實現。
由於會用到多執行緒,JAVA中對於多執行緒感覺就兩種。一種是常用的runnable還有一種是callable。
主要區別是runnable無返回值,callable有返回值。考慮到SQL查詢會有結果,需要把查詢的結果拿來進行彙總,故決定採用callable來進行實現。
與Callable相關的類:
Future是Callable返回的結果
FutureTask是對Future的一個封裝,讓執行Future更方便點.
實現的程式碼主要如下:
/** * @param keyList 鍵值list * @return 結果, 多執行緒查詢並彙總 */ public List<String> getValueListByKeyListSharding(List<String> keyList) { long startTime = System.currentTimeMillis(); log.info("MYSQL查詢mul開始時間:" + startTime); //為提高響應速度,每次分片查詢並彙總 final int shardingCount = 600;//每600條為一個分片 if (keyList.size() > shardingCount) { Map<Integer, List<String>> resultMap = new TreeMap<Integer, List<String>>(); List<Future<Map<Integer, List<String>>>> futureTaskList = new ArrayList<>(); final ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i <= keyList.size() / shardingCount; i++) { //每次都得到一個subList List<String> subList = new ArrayList<>(); if ((i + 1) * shardingCount > keyList.size()) { subList = keyList.subList(i * shardingCount, keyList.size()); } else { subList = keyList.subList(i * shardingCount, shardingCount * (i + 1)); } //拿著這個subList去DB進行查詢,多執行緒 final List<String> finalSubList = subList; final int finalI = i; Callable<Map<Integer, List<String>>> queryCall = new Callable<Map<Integer, List<String>>>() { @Override public Map<Integer, List<String>> call() throws Exception { Map<Integer, List<String>> subResultMap = new TreeMap<Integer, List<String>>(); subResultMap.put(finalI, getValueListByKeyList(new CopyOnWriteArrayList<String>(finalSubList))); return subResultMap; } }; //新增到task中,執行查詢 FutureTask<Map<Integer, List<String>>> futureTask = new FutureTask<Map<Integer, List<String>>>(queryCall); futureTaskList.add(futureTask); executorService.submit(futureTask);//執行查詢 } //待所有查詢完畢後,再進行順序彙總 for (Future<Map<Integer, List<String>>> futureTask : futureTaskList) { try { Map<Integer, List<String>> integerListMap = futureTask.get(); resultMap.putAll(integerListMap); } catch (Exception e) { e.printStackTrace(); } } //進行結果彙總 List<String> resultList = new ArrayList<>(); Set<Integer> mapKeys = resultMap.keySet(); for (Integer mapItem : mapKeys) { resultList.addAll(resultMap.get(mapItem)); } long endTime = System.currentTimeMillis(); log.info("本次mul資料查詢(mysql)耗時:" + (endTime - startTime)); log.info("本次mul資料查詢(mysql)結束時間:" + endTime); executorService.shutdown();//關閉執行緒池 return resultList; } else { return getValueListByKeyList(keyList); // return getValueListByKeyList(new CopyOnWriteArrayList<String>(keyList)); } }
其中getValueListByKeyList是一個進行資料查詢的具體方法
/** * @param keyList 鍵值list * @return valueList, 如果沒有這個值或這條記錄,也需要返回一個空串在list中 */ public List<String> getValueListByKeyList(List<String> keyList) { long startTime = System.currentTimeMillis(); log.info("MYSQL查詢開始時間:" + startTime); String sql = "SELECT `key`,`value` FROM tb_super where `KEY` in (:ids)"; JdbcTemplate jdbcTemplate = getJdbcTemplate(); NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); MapSqlParameterSource parameters = new MapSqlParameterSource(); parameters.addValue("ids", keyList); List<Map<String, String>> list = namedParameterJdbcTemplate.queryForList(sql, parameters); List<String> resultList = new ArrayList<>(); //<editor-fold desc="通過迴圈迭代遍歷,獲取出對應的資料"> // Iterator<String> resultIterator = keyList.iterator(); // while (resultIterator.hasNext()) { // String keyItem = resultIterator.next(); // boolean hasValue = false; // Iterator<Map<String, String>> mapIterator = list.iterator(); // MARKER: // while (mapIterator.hasNext()) { // Map<String, String> mapItem = mapIterator.next(); // String key = mapItem.get("key") == null ? "" : mapItem.get("key"); // String value = mapItem.get("value") == null ? "" : mapItem.get("value"); // if (keyItem.equals(key)) {//說明這條KEY有查詢結果 // resultList.add(value); // hasValue = true; // //在兩個集合中都要去掉這個已經查詢到的資料節點 // resultIterator.remove(); // mapIterator.remove(); // continue MARKER;//跳出,進行外層迴圈 // } // } // if (!hasValue) { // resultList.add("");//說明沒有查詢結果,則賦空值 // } // } //</editor-fold> //<editor-fold desc="全部遍歷方式"> for (String keyItem : keyList) { boolean hasValue = false; MARKER: for (Map<String, String> listItem : list) { String key = listItem.get("key") == null ? "" : listItem.get("key"); String value = listItem.get("value") == null ? "" : listItem.get("value"); if (keyItem.equals(key)) {//說明這條KEY有查詢結果 resultList.add(value); hasValue = true; continue MARKER;//跳出,進行外層迴圈 } } if (!hasValue) { resultList.add("");//說明沒有查詢結果,則賦空值 } } //</editor-fold> long endTime = System.currentTimeMillis(); log.info("本次資料查詢(mysql)耗時:" + (endTime - startTime)); log.info("本次資料查詢(mysql)結束時間:" + endTime); return resultList; }
查詢相同的資料,以前沒有拆分時,需要10S左右,現在查詢時,只需要3S左右就可以查完了。相比以前,查詢還是有了很大的提高。