1. 程式人生 > >多執行緒之futureTask(future,callable)例項,jdbc資料多執行緒查詢

多執行緒之futureTask(future,callable)例項,jdbc資料多執行緒查詢

最近遇到一個這樣的功能要求。在查詢資料時,由於查詢的資料量比較大,一次查詢(一條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左右就可以查完了。相比以前,查詢還是有了很大的提高。