需求:在開發業務報表時,需要從MySQL數據庫讀取數據后進行操作,然后寫入數據庫,使用定時任務跑批。
分析:①兼顧性能,② MySQL沒有Oracle那么方便、強大的存儲過程。綜上所述,使用線程池以分批提交的方案來插入MySQL數據庫,對於更新操作、在Java端進行分頁等的操作,本方案也支持。代碼如下:
1 private void batchDeal(List data, int batchNum) throws InterruptedException { 2 int totalNum = data.size(); 3 int pageNum = totalNum % batchNum == 0 ? totalNum / batchNum : totalNum / batchNum + 1; 4 ExecutorService executor = Executors.newFixedThreadPool(pageNum);
5 try { 6 CountDownLatch countDownLatch = new CountDownLatch(pageNum); 7 List subData = null; 8 int fromIndex, toIndex; 9 for (int i = 0; i < pageNum; i++) { 10 fromIndex = i * batchNum; 11 toIndex = Math.min(totalNum, fromIndex + batchNum); 12 subData = data.subList(fromIndex, toIndex); 13 ImportTask task = new ImportTask(subData, countDownLatch); 14 executor.execute(task); 15 } 16 // 主線程必須在啟動其它線程后立即調用CountDownLatch.await()方法, 17 // 這樣主線程的操作就會在這個方法上阻塞,直到其它線程完成各自的任務。 18 // 計數器的值等於0時,主線程就能通過await()方法恢復執行自己的任務。
19 countDownLatch.await(); 20 logger.info("數據操作完成!可以在此開始其它業務"); 21 } finally { 22 // 關閉線程池,釋放資源
23 executor.shutdown(); 24 } 25 } 26
27 class ImportTask implements Runnable { 28 private List list; 29 private CountDownLatch countDownLatch; 30
31 public ImportTask(List data, CountDownLatch countDownLatch) { 32 this.list = data; 33 this.countDownLatch = countDownLatch; 34 } 35
36 @Override 37 public void run() { 38 if (null != list) { 39 // 業務邏輯,例如批量insert或者update
40 logger.info("現在操作的數據是{}", list); 41 } 42 // 發出線程任務完成的信號
43 countDownLatch.countDown(); 44 } 45 }
在第1行中,List data表示傳入的數據,batchNum表示每一次處理的數量,例如500條等。