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就得從入門到入土,最終要的就是處理問題的時候態度以及思路,不要急躁,那樣反而會更亂,慢慢來總會解決的,加油!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/302362.html
標籤:其他
