日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

批量任务体现多线程的威力!

發布時間:2025/7/14 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 批量任务体现多线程的威力! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景

對于多線程的理解不是非常深刻,工作中用到多線程代碼的機會也不多,前不久遇到了一個使用場景,通過編碼實現后對于多線程的理解和應用有了更加深刻的理解。場景如下:現有給用戶發送產品調研的需求,運營的同事拿來了一個Excel文件,要求給Excel里面大約六萬個手機號發送調研短信。

最簡單的方法就是一個循環然后單線程順序發送,但是核心問題在于,給短信運營商發短信的接口響應時間較長,假設平均100ms的響應時間,那么單線程發送的話需要6萬*0.1秒=6000秒。顯然這個時間是不能接受的,運營商系統的發送接口我們是不能優化的,只得增強自己的發送和處理能力才能盡快的完成任務。

批量發短信

讀取Excel中的信息

包依賴

工具類代碼,Maven中引入如下兩個包

<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17</version> </dependency> <dependency><groupId>org.apache.xmlbeans</groupId><artifactId>xmlbeans</artifactId><version>2.6.0</version> </dependency>

讀取Excel的工具類代碼

/*** 讀取Excel的文件信息** @param fileName*/ public static void readFromExcel(String fileName) {InputStream is = null;try {is = new FileInputStream(fileName);XSSFWorkbook workbook = new XSSFWorkbook(is);XSSFSheet sheet = workbook.getSheetAt(0);int num = 0;// 循環行Rowfor (int rowNum = 0, lastNum = sheet.getLastRowNum(); rowNum <= lastNum; rowNum++) {XSSFRow row = sheet.getRow(rowNum);String phoneNumber = getStringValueFromCell(row.getCell(0)).trim();phoneList.add(phoneNumber);}System.out.println(num);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} }/*** 讀取Excel里面Cell內容** @param cell* @return*/ private static String getStringValueFromCell(XSSFCell cell) {// 單元格內的時間格式SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");// 單元格內的數字類型DecimalFormat decimalFormat = new DecimalFormat("#.#####");// 單元格默認為空String cellValue = "";if (cell == null) {return cellValue;}// 按類型讀取if (cell.getCellType() == XSSFCell.CELL_TYPE_STRING) {cellValue = cell.getStringCellValue();} else if (cell.getCellType() == XSSFCell.CELL_TYPE_NUMERIC) {// 日期轉為時間形式if (DateUtil.isCellDateFormatted(cell)) {double d = cell.getNumericCellValue();Date date = DateUtil.getJavaDate(d);cellValue = dateFormat.format(date);} else {// 其他轉為數字cellValue = decimalFormat.format((cell.getNumericCellValue()));}} else if (cell.getCellType() == XSSFCell.CELL_TYPE_BLANK) {cellValue = "";} else if (cell.getCellType() == XSSFCell.CELL_TYPE_BOOLEAN) {cellValue = String.valueOf(cell.getBooleanCellValue());} else if (cell.getCellType() == XSSFCell.CELL_TYPE_ERROR) {cellValue = "";} else if (cell.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {cellValue = cell.getCellFormula().toString();}return cellValue; }  

模擬運營商發送短信的方法

/*** 外部接口耗時長,通過多線程增強** @param userPhone*/ public void sendMsgToPhone(String userPhone) {try {Thread.sleep(SEND_COST_TIME);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("send message to : " + userPhone); }

多線程發短信

簡單的單線程發送

/*** 單線程發送** @param phoneList* @return*/private long singleThread(List<String> phoneList) {long start = System.currentTimeMillis();/*// 直接主線程執行for (String phoneNumber : phoneList) {threadOperation.sendMsgToPhone(phoneNumber);}*/SendMsgExtendThread smet = threadOperation.new SendMsgExtendThread(phoneList);smet.start();long totalTime = System.currentTimeMillis() - start;System.out.println("單線程發送總時間:" + totalTime);return totalTime;}

對于大批量發短信的場景,如果使用單線程將全部一千個號碼發送完畢的話,大約需要103132ms,可見效率低下,耗費時間較長。

多線程發送短信中的一個核心要點是,將全部手機號碼拆分成多個組后,分配給每個線程進行執行。

兩個線程的示例

/*** 兩個線程發送** @param phoneList* @return*/ private long twoThreads(List<String> phoneList) {long start = System.currentTimeMillis();List<String> list1 = phoneList.subList(0, phoneList.size() / 2);List<String> list2 = phoneList.subList(phoneList.size() / 2, phoneList.size());SendMsgExtendThread smet = threadOperation.new SendMsgExtendThread(list1);smet.start();SendMsgExtendThread smet1 = threadOperation.new SendMsgExtendThread(list2);smet1.start();return 0; }

另一種數據分組方式

/*** 另外一種分配方式** @param phoneList*/ private void otherThread(List<String> phoneList) {for (int threadNo = 0; threadNo < 10; threadNo++) {int numbersPerThread = 10;List<String> list = phoneList.subList(threadNo * numbersPerThread, (threadNo * numbersPerThread) + 10);SendMsgExtendThread smet = threadOperation.new SendMsgExtendThread(list);smet.start();if (list.size() < numbersPerThread) {break;}} }

線程池發送

/*** 線程池發送** @param phoneList* @return*/ private void threadPool(List<String> phoneList) {for (int threadNo = 0; threadNo < THREAD_POOL_SIZE; threadNo++) {int numbersPerThread = 10;List<String> list = phoneList.subList(threadNo * numbersPerThread, (threadNo * numbersPerThread) + 10);threadOperation.executorService.execute(threadOperation.new SendMsgExtendThread(list));}threadOperation.executorService.shutdown(); }

使用Callable發送

/*** 多線程發送** @param phoneList* @return*/ private void multiThreadSend(List<String> phoneList) {List<Future<Long>> futures = new ArrayList<>();for (int threadNo = 0; threadNo < THREAD_POOL_SIZE; threadNo++) {int numbersPerThread = 100;List<String> list = phoneList.subList(threadNo * numbersPerThread, (threadNo * numbersPerThread) + 100);Future<Long> future = threadOperation.executorService.submit(threadOperation.new SendMsgImplCallable(list, String.valueOf(threadNo)));futures.add(future);}for (Future<Long> future : futures) {try {System.out.println(future.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}threadOperation.executorService.shutdown(); }

使用多線程發送,將發送任務進行分割然后分配給每個線程執行,執行完畢需要10266ms,可見執行效率明顯提升,消耗時間明顯縮短。

完整代碼

package com.lingyejun.tick.authenticator;import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.*;public class ThreadOperation {// 發短信的同步等待時間private static final long SEND_COST_TIME = 100L;// 手機號文件private static final String FILE_NAME = "/Users/lingye/Downloads/phone_number.xlsx";// 手機號列表private static List<String> phoneList = new ArrayList<>();// 單例對象private static volatile ThreadOperation threadOperation;// 線程個數private static final int THREAD_POOL_SIZE = 10;// 初始化線程池private ExecutorService executorService = new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());public ThreadOperation() {// 從本地文件中讀取手機號碼readFromExcel(FILE_NAME);}public static void main(String[] args) {ThreadOperation threadOperation = getInstance();//threadOperation.singleThread(phoneList);threadOperation.multiThreadSend(phoneList);}/*** 單例獲取對象** @return*/public static ThreadOperation getInstance() {if (threadOperation == null) {synchronized (ThreadOperation.class) {if (threadOperation == null) {threadOperation = new ThreadOperation();}}}return threadOperation;}/*** 讀取Excel的文件信息** @param fileName*/public static void readFromExcel(String fileName) {InputStream is = null;try {is = new FileInputStream(fileName);XSSFWorkbook workbook = new XSSFWorkbook(is);XSSFSheet sheet = workbook.getSheetAt(0);int num = 0;// 循環行Rowfor (int rowNum = 0, lastNum = sheet.getLastRowNum(); rowNum <= lastNum; rowNum++) {XSSFRow row = sheet.getRow(rowNum);String phoneNumber = getStringValueFromCell(row.getCell(0)).trim();phoneList.add(phoneNumber);}System.out.println(num);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 讀取Excel里面Cell內容** @param cell* @return*/private static String getStringValueFromCell(XSSFCell cell) {// 單元格內的時間格式SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");// 單元格內的數字類型DecimalFormat decimalFormat = new DecimalFormat("#.#####");// 單元格默認為空String cellValue = "";if (cell == null) {return cellValue;}// 按類型讀取if (cell.getCellType() == XSSFCell.CELL_TYPE_STRING) {cellValue = cell.getStringCellValue();} else if (cell.getCellType() == XSSFCell.CELL_TYPE_NUMERIC) {// 日期轉為時間形式if (DateUtil.isCellDateFormatted(cell)) {double d = cell.getNumericCellValue();Date date = DateUtil.getJavaDate(d);cellValue = dateFormat.format(date);} else {// 其他轉為數字cellValue = decimalFormat.format((cell.getNumericCellValue()));}} else if (cell.getCellType() == XSSFCell.CELL_TYPE_BLANK) {cellValue = "";} else if (cell.getCellType() == XSSFCell.CELL_TYPE_BOOLEAN) {cellValue = String.valueOf(cell.getBooleanCellValue());} else if (cell.getCellType() == XSSFCell.CELL_TYPE_ERROR) {cellValue = "";} else if (cell.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {cellValue = cell.getCellFormula().toString();}return cellValue;}/*** 外部接口耗時長,通過多線程增強** @param userPhone*/public void sendMsgToPhone(String userPhone) {try {Thread.sleep(SEND_COST_TIME);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("send message to : " + userPhone);}/*** 單線程發送** @param phoneList* @return*/private long singleThread(List<String> phoneList) {long start = System.currentTimeMillis();/*// 直接主線程執行for (String phoneNumber : phoneList) {threadOperation.sendMsgToPhone(phoneNumber);}*/SendMsgExtendThread smet = threadOperation.new SendMsgExtendThread(phoneList);smet.start();long totalTime = System.currentTimeMillis() - start;System.out.println("單線程發送總時間:" + totalTime);return totalTime;}/*** 另外一種分配方式** @param phoneList*/private void otherThread(List<String> phoneList) {for (int threadNo = 0; threadNo < 10; threadNo++) {int numbersPerThread = 10;List<String> list = phoneList.subList(threadNo * numbersPerThread, (threadNo * numbersPerThread) + 10);SendMsgExtendThread smet = threadOperation.new SendMsgExtendThread(list);smet.start();if (list.size() < numbersPerThread) {break;}}}/*** 兩個線程發送** @param phoneList* @return*/private long twoThreads(List<String> phoneList) {long start = System.currentTimeMillis();List<String> list1 = phoneList.subList(0, phoneList.size() / 2);List<String> list2 = phoneList.subList(phoneList.size() / 2, phoneList.size());SendMsgExtendThread smet = threadOperation.new SendMsgExtendThread(list1);smet.start();SendMsgExtendThread smet1 = threadOperation.new SendMsgExtendThread(list2);smet1.start();return 0;}/*** 線程池發送** @param phoneList* @return*/private void threadPool(List<String> phoneList) {for (int threadNo = 0; threadNo < THREAD_POOL_SIZE; threadNo++) {int numbersPerThread = 10;List<String> list = phoneList.subList(threadNo * numbersPerThread, (threadNo * numbersPerThread) + 10);threadOperation.executorService.execute(threadOperation.new SendMsgExtendThread(list));}threadOperation.executorService.shutdown();}/*** 多線程發送** @param phoneList* @return*/private void multiThreadSend(List<String> phoneList) {List<Future<Long>> futures = new ArrayList<>();for (int threadNo = 0; threadNo < THREAD_POOL_SIZE; threadNo++) {int numbersPerThread = 100;List<String> list = phoneList.subList(threadNo * numbersPerThread, (threadNo * numbersPerThread) + 100);Future<Long> future = threadOperation.executorService.submit(threadOperation.new SendMsgImplCallable(list, String.valueOf(threadNo)));futures.add(future);}for (Future<Long> future : futures) {try {System.out.println(future.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}threadOperation.executorService.shutdown();}public class SendMsgExtendThread extends Thread {private List<String> numberListByThread;public SendMsgExtendThread(List<String> numberList) {numberListByThread = numberList;}@Overridepublic void run() {long startTime = System.currentTimeMillis();for (int i = 0; i < numberListByThread.size(); i++) {System.out.print("no." + (i + 1));sendMsgToPhone(numberListByThread.get(i));}System.out.println("== single thread send " + numberListByThread.size() + "execute time:" + (System.currentTimeMillis() - startTime) + " ms");}}public class SendMsgImplCallable implements Callable<Long> {private List<String> numberListByThread;private String threadName;public SendMsgImplCallable(List<String> numberList, String threadName) {numberListByThread = numberList;this.threadName = threadName;}@Overridepublic Long call() throws Exception {Long startMills = System.currentTimeMillis();for (String number : numberListByThread) {sendMsgToPhone(number);}Long endMills = System.currentTimeMillis();return endMills - startMills;}} }

總結

以上是生活随笔為你收集整理的批量任务体现多线程的威力!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。