Java-用線程池以及CountDownLatch優化代碼 提高執行效率



1. 問題描述

客戶提了一個新需求,開發完成后發現查詢一小時內的數據耗時要 7 秒,這客戶肯定不滿意,不滿意就要和領導提,領導不開心了我就要被扣工資!所以就想利用線程池優化一下代碼,提高方法的效率。

2. 初始代碼

點擊查看代碼

        // 查詢所有站點
        QueryWrapper<Station> stationQW = new QueryWrapper<>();
        stationQW.lambda().eq(Station::getRegionCode, region);
        List<Station> stations = this.stationMapper.selectList(stationQW);
        List<StationPO> stationPOList = new ArrayList<>();
        long start = System.currentTimeMillis();
        for (Station station : stations) {
            long methodStart = System.currentTimeMillis();
            String stationCode = station.getStationCode();
            StationPO stationPO = new StationPO();
            BeanUtils.copyProperties(station, stationPO);
            // 總雨量
            Float rainFall = stationDataMapper.queryRainFallByTime(startTime, endTime, stationCode);
            stationPO.setRainFall(rainFall);
            // 平均氣溫
            Float avgTemp = stationDataMapper.queryAvgTempByTime(startTime, endTime, stationCode);
            stationPO.setAvgTemp(avgTemp);
            // 最高氣溫
            Float maxTemp = stationDataMapper.queryMaxTempByTime(startTime, endTime, stationCode);
            stationPO.setMaxTemp(maxTemp);
            // 最低氣溫
            Float minTemp = stationDataMapper.queryMinTempByTime(startTime, endTime, stationCode);
            stationPO.setMinTemp(minTemp);
            // 最大風速
            Float maxWind = stationDataMapper.queryMaxWindByTime(startTime, endTime, stationCode);
            stationPO.setMaxWind(maxWind);
            // 極大風速
            Float enormousWind = stationDataMapper.queryEnormousWindByTime(startTime, endTime, stationCode);
            stationPO.setEnormousWind(enormousWind);
            // 平均濕度
            Float avgHumidity = stationDataMapper.queryAvgHumidityByTime(startTime, endTime, stationCode);
            stationPO.setAvgHumidity(avgHumidity);
            long methodEnd = System.currentTimeMillis();
            System.out.println("7個查詢耗時:" + new BigDecimal(methodEnd - methodStart).divide(new BigDecimal(1000)).doubleValue());
            stationPOList.add(stationPO);
        }
        long end = System.currentTimeMillis();
        System.out.println("方法耗時:" + new BigDecimal(end - start).divide(new BigDecimal(1000)).doubleValue());
       
我這邊站點數據集合的大小是37,每次循環都有7個SQL語句,每個SQL的執行時間在0.8秒左右,時間都浪費在循環上了,所以設想循環都創建一個線程去執行任務,這樣的話總耗時也就是一次循環的時間。

3. 用到的技術

  • ThreadPoolExecutor線程池
  • CountDownLatch鎖
這里簡單說一下CountDownLatch鎖,作用就是一個線程會等待其他線程都執行完畢后再繼續執行,具體是通過一個計數器來實現的,計數器的初始值是線程的數量。每當一個線程執行完畢后,計數器的值就-1,當計數器的值為0時,表示所有線程都執行完畢,然后在閉鎖上等待的線程就可以恢復工作了。

4. 整體思路

首先創建一個線程池,然后創建鎖,這里我直接把線程池的大小以及鎖的count都設置成list的大小,也就是循環次數,開始循環,for循環開啟線程,執行一個站點的查詢數據SQL,查詢完成后關閉一個鎖(countDown方法)。循環外面等待所有線程結束后(await方法),關閉線程池(shutdown方法),結束。

5. 優化后代碼

點擊查看代碼

        // 查詢所有站點
        QueryWrapper<Station> stationQW = new QueryWrapper<>();
        stationQW.lambda().eq(Station::getRegionCode, region);
        List<Station> stations = this.stationMapper.selectList(stationQW);
        List<StationPO> stationPOList = new ArrayList<>();
        ThreadPoolExecutor poolExecutor = ExecutorBuilder.create()
                .setCorePoolSize(stations.size()) // 初始線程
                .setMaxPoolSize(stations.size()) // 最大線程
                .setWorkQueue(new LinkedBlockingQueue<>(100)) // 線程池策略
                .build();
        CountDownLatch cdl = new CountDownLatch(stations.size());
        long start = System.currentTimeMillis();
        for (Station station : stations) {
            poolExecutor.execute(
                    () -> {
                        long methodStart = System.currentTimeMillis();
                        String stationCode = station.getStationCode();
                        StationPO stationPO = new StationPO();
                        BeanUtils.copyProperties(station, stationPO);
                        // 總雨量
                        Float rainFall = stationDataMapper.queryRainFallByTime(startTime, endTime, stationCode);
                        stationPO.setRainFall(rainFall);
                        // 平均氣溫
                        Float avgTemp = stationDataMapper.queryAvgTempByTime(startTime, endTime, stationCode);
                        stationPO.setAvgTemp(avgTemp);
                        // 最高氣溫
                        Float maxTemp = stationDataMapper.queryMaxTempByTime(startTime, endTime, stationCode);
                        stationPO.setMaxTemp(maxTemp);
                        // 最低氣溫
                        Float minTemp = stationDataMapper.queryMinTempByTime(startTime, endTime, stationCode);
                        stationPO.setMinTemp(minTemp);
                        // 最大風速
                        Float maxWind = stationDataMapper.queryMaxWindByTime(startTime, endTime, stationCode);
                        stationPO.setMaxWind(maxWind);
                        // 極大風速
                        Float enormousWind = stationDataMapper.queryEnormousWindByTime(startTime, endTime, stationCode);
                        stationPO.setEnormousWind(enormousWind);
                        // 平均濕度
                        Float avgHumidity = stationDataMapper.queryAvgHumidityByTime(startTime, endTime, stationCode);
                        stationPO.setAvgHumidity(avgHumidity);
                        long methodEnd = System.currentTimeMillis();
                        System.out.println("7個查詢耗時:" + new BigDecimal(methodEnd - methodStart).divide(new BigDecimal(1000)).doubleValue());
                        stationPOList.add(stationPO);
                        // 閉鎖-1
                        cdl.countDown();
                    }
            );
        }
        try {
            // 等待所有線程結束
            cdl.await();
        } catch (InterruptedException e) {
            StaticLog.error("線程錯誤:{}",e.getMessage());
        }
        poolExecutor.shutdown();
        long end = System.currentTimeMillis();
        System.out.println("方法耗時:" + new BigDecimal(end - start).divide(new BigDecimal(1000)).doubleValue());

6. 自我總結

由於是第一次使用多線程,遇到了很多問題,雖然最后查詢時間縮短到 0.7 秒左右,但是不知道這樣使用多線程合不合理,后續還要繼續學習優化。通過這次開發也發現了自己知識儲備量的不足,果然啊,學Java就得從入門到入土。最終要的就是處理問題的時候態度以及思路,不要急躁,那樣反而會更亂,慢慢來總會解決的,加油!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM