自定義線程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5));
第一個參數:核心線程池大小,默認創建後就不會銷燬,需要設置allowCoreThreadTimeOut爲true時會銷燬
第二個參數:線程池最大大小
第三個參數:線程池最大空閒時間(線程空閒下來多久自動釋放)
第四個參數:時間單位
第五個參數:線程阻塞隊列
- 對上圖的詳解
1 提交任務到線程池:executor.execute(new Runable());
2 判斷核心線程池大小是否已滿,否,執行任務
3 是,判斷隊列是否已滿,否,進行等待隊列
4 是,判斷最大線程池大小是否已滿,否,執行任務
5 是,執行拒絕策略,對外就是拋出異常
提供的現成可使用的線程池
Executors.newCachedThreadPool(無界線程池,自動線程回收)
Executors.newFixedThreadPool(固定大小的線程池)
Executors.newSingleThreadExecutor(單一後臺線程)
我做過的真實案例-> 線程池應用
-
場景:數據庫大量數據(百萬級)需要同步至Elasticsearch庫,目前採用的單線程的方式,每次從數據庫中讀取5000條數據同步至Elasticsearch庫,循環n次,直到數據庫讀取不到數據則同步完成。 缺點:此過程需要2小時時間。
-
改造:需要將同步時間縮短,同步不能使ES崩掉
-
方案:採用線程池的方式,配置如下:
核心線程池大小爲2 最大線程池大小爲2 線程池最大空閒時間 0秒 隊列大小 1000, 隊列大小的選擇:1000 * 5000 = 500萬 數據不超過5百萬
-
實現代碼:每同步完成一次需要返回具體插入了ES庫的條數,然後實現累加可算出本次同步一共同步了多少條,JDK提供了Future對象
do { List<JSONObject> list = cilSettOracleMapper.findCilSettByDate(sysDate, page * SIZE, (page + 1) * SIZE); if(list != null && list.size() > 0){ Future<Long> future = ThreadManager.executorService.submit(new Callable<Long>() { @Override public Long call(){ log.debug("線程名:" + Thread.currentThread().getName() + "-->" + bulkRequestSize); return ESClient.getESClient().indexCilSettOracle(request, list, scanTransService, merInfoMapper, bulkRequestSize); } }); futrueList.add(future); }else{ break; } log.info("indexCilSett page:" + page + ",bulkRequestSize: " + bulkRequestSize); page++; } while (true); Long futureRequestSize = 0L; for(Future<Long> f : futrueList) { try { futureRequestSize += f.get(); } catch (InterruptedException | ExecutionException e) { log.error("執行Future的get方法發生異常", e); } }
-
上述注意部分:第一次接觸Future類,當時直接在循環裏調用future.get()方法獲取數據,發現線程是一個一個按照順序去執行,後來瞭解到get()方法會阻塞線程,於是有了上述的先將Future對象有list存起來,然後循環拿結果。
-
Future類講解的比較好的:https://www.cnblogs.com/dolphin0520/p/3949310.html