background

I don’t have a very deep understanding of multi-threading, and I don’t have many opportunities to use multi-threaded code in my work. I met a usage scenario recently, and I have a deeper understanding of multi-threading and application after coding. The scenario is as follows: There is a need to send product research to users, and an operation colleague brings an Excel file and asks to send research short messages to about 60,000 mobile phone numbers in Excel.

The simplest method is to send messages in a single thread order through a loop, but the core problem is that the response time of the interface for sending SMS messages to the SMS carrier is long. Assuming an average response time of 100ms, it takes 60,000 *0.1 seconds =6000 seconds for a single thread to send SMS messages. Obviously, this time is unacceptable. We cannot optimize the sending interface of the operator system, so we have to enhance our sending and processing capabilities to complete the task as soon as possible.

Batch texting

Read information in Excel

Package depends on

Utility class code, Maven introduces the following two packages

<dependency> <groupId>org.apache.poi</groupId> <artifactId> Poi-ooxml </artifactId> <version>3.17</version> </dependency> < the dependency > < groupId > org, apache xmlbeans < / groupId > < artifactId > xmlbeans < / artifactId > < version > server < / version > </dependency>Copy the code

Read Excel utility class code

** @param fileName */ public static voidreadFromExcel(String fileName) { InputStream is = null; try { is = new FileInputStream(fileName); XSSFWorkbook workbook = new XSSFWorkbook(is); XSSFSheet sheet = workbook.getSheetAt(0); int num = 0; // Loop 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(); }} /** * Read the Cell content in Excel ** @param Cell * @return
 */
private static String getStringValueFromCell(XSSFCell cell) {
 
     if (cell == null) {
        returnnull; SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); DecimalFormat = new DecimalFormat("# # # # # #"); // Cells default to empty String cellValue =""; // Read by typeif (cell.getCellType() == XSSFCell.CELL_TYPE_STRING) {
        cellValue = cell.getStringCellValue();
    } else if(cell.getcelltype () == xssfcell.cell_type_numeric) {// The date is converted to timeif (DateUtil.isCellDateFormatted(cell)) {
            double d = cell.getNumericCellValue();
            Date date = DateUtil.getJavaDate(d);
            cellValue = dateFormat.format(date);
        } elseCellValue = 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();
    }
    returncellValue; }&emsp; &emsp;Copy the code

Simulate the method of sending SMS messages by the carrier

/** * The external interface takes a long time. Public void sendMsgToPhone(String userPhone) {try {thread.sleep (SEND_COST_TIME); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("send message to : " + userPhone);
}
Copy the code

Multi-threaded SMS

Simple single thread send

/** * single thread sends ** @param phoneList * @return*/ private long singleThread(List<String> phoneList) { long start = System.currentTimeMillis(); /*/ execute directly to the main threadfor (String phoneNumber : phoneList) {
            threadOperation.sendMsgToPhone(phoneNumber);
        }*/
        SendMsgExtendThread smet = threadOperation.new SendMsgExtendThread(phoneList);
        smet.start();
        long totalTime = System.currentTimeMillis() - start;
        System.out.println("Total time for single thread to send:" + totalTime);
        return totalTime;
    }
Copy the code

For large-volume SMS sending scenario, it takes about 103132ms to send all one thousand numbers using a single thread, which shows low efficiency and long time consumption.

One of the core points of multi-threaded SMS is to divide all mobile phone numbers into multiple groups and assign them to each thread for execution.

A two-thread example

/** * two threads send ** @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;
}
Copy the code

Another way of grouping data

/** @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; }}}Copy the code

Thread pool sending

/** * Thread pool sends ** @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();
}
Copy the code

Send using Callable

/** * multithreaded send ** @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();
}

Copy the code

Using multi-threading to send, the sending task is divided and then assigned to each thread for execution. 10266ms is required after execution, which shows that the execution efficiency is significantly improved and the consumption time is significantly shortened.

The complete code

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 Long SEND_COST_TIME = 100L; // Mobile number file private static final String FILE_NAME ="/Users/lingye/Downloads/phone_number.xlsx"; Private static List<String> phoneList = new ArrayList<>(); 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>()); publicThreadOperation() {// Read the phone number from the local filereadFromExcel(FILE_NAME); } public static void main(String[] args) { ThreadOperation threadOperation = getInstance(); //threadOperation.singleThread(phoneList); threadOperation.multiThreadSend(phoneList); } /** * singleton gets object ** @return
     */
    public static ThreadOperation getInstance() {
        if (threadOperation == null) {
            synchronized (ThreadOperation.class) {
                if(threadOperation == null) { threadOperation = new ThreadOperation(); }}}returnthreadOperation; } /** * read Excel file information ** @param fileName */ public static voidreadFromExcel(String fileName) { InputStream is = null; try { is = new FileInputStream(fileName); XSSFWorkbook workbook = new XSSFWorkbook(is); XSSFSheet sheet = workbook.getSheetAt(0); int num = 0; // Loop 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(); }} /** * Read the Cell content in Excel ** @param Cell * @return
     */
    private static String getStringValueFromCell(XSSFCell cell) {
 
         if (cell == null) {
            returnnull; SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); DecimalFormat = new DecimalFormat("# # # # # #"); // Cells default to empty String cellValue =""; // Read by typeif (cell.getCellType() == XSSFCell.CELL_TYPE_STRING) {
            cellValue = cell.getStringCellValue();
        } else if(cell.getcelltype () == xssfcell.cell_type_numeric) {// The date is converted to timeif (DateUtil.isCellDateFormatted(cell)) {
                double d = cell.getNumericCellValue();
                Date date = DateUtil.getJavaDate(d);
                cellValue = dateFormat.format(date);
            } elseCellValue = 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();
        }
        returncellValue; } /** * The external interface takes a long time. Public void sendMsgToPhone(String userPhone) {try {thread.sleep (SEND_COST_TIME); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("send message to : "+ userPhone); } /** * single thread sends ** @param phoneList * @return*/ private long singleThread(List<String> phoneList) { long start = System.currentTimeMillis(); /*/ execute directly to the main threadfor (String phoneNumber : phoneList) {
            threadOperation.sendMsgToPhone(phoneNumber);
        }*/
        SendMsgExtendThread smet = threadOperation.new SendMsgExtendThread(phoneList);
        smet.start();
        long totalTime = System.currentTimeMillis() - start;
        System.out.println("Total time for single thread to send:" + totalTime);
        returntotalTime; } /** * @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; }}} /** * Two threads send ** @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();
        return0; } /** * thread pool sends ** @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(); } /** * multithreaded send ** @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;
        }
 
        @Override
        public 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;
        }
 
        @Override
        public Long call() throws Exception {
            Long startMills = System.currentTimeMillis();
            for (String number : numberListByThread) {
                sendMsgToPhone(number);
            }
            Long endMills = System.currentTimeMillis();
            returnendMills - startMills; }}}Copy the code