多線程處理容器ExecutorCompletionService使用

一、多線程的好處:

接口調用,往往離不開多線程,可以減少響應時間

二、場景:

1、業務A有1W個條形碼需要調用第三方(商品服務)獲取商品信息接口

2、第三方(商品服務)獲取商品信息接口, 單次只允許傳入10個條形碼來查詢

三、代碼:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * 描    述:多線程處理容器(菜鳥直接用即可,儘量不要改動這裏)
 * 作    者:java瀟邦
 * 時    間:2016-09-01
 */
@Component
public class BatchContainerExecutor {

    private static final Logger logger = LoggerFactory.getLogger(BatchContainerExecutor.class);

    private static ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

    /**
     * 功    能:併發處理taskList中的任務
     * 作    者:java瀟邦
     * 時    間:2016-09-01
     */
    public List<Object> execute(List<Callable<Object>> taskList) {
        int taskSize = taskList.size();
        List<Object> results = new ArrayList<Object>(taskSize);
        ExecutorCompletionService<Object> concurrentExecutor = new ExecutorCompletionService<Object>(pool);
        for (Callable<Object> callable : taskList) {
            concurrentExecutor.submit(callable);
        }
        for (int i = 0; i < taskSize; i++) {
            Object result = getQuenueMsg(concurrentExecutor);
            results.add(result);
        }
        return results;
    }

    /**
     * 功    能:併發處理taskList中的任務***如果有任務發生異常,則取消剩餘任務
     * 作    者:java瀟邦
     * 時    間:2016-09-01
     */
    public List<Object> execute(List<Callable<Object>> taskList, ExecutorService pool) {
        int taskSize = taskList.size();
        List<Object> results = new ArrayList<Object>(taskSize);
        List<Future<Object>> futures = new ArrayList<Future<Object>>(taskSize);
        ExecutorCompletionService<Object> concurrentExecutor = new ExecutorCompletionService<Object>(pool);
        for (Callable<Object> callable : taskList) {
            futures.add(concurrentExecutor.submit(callable));
        }
        boolean flag = false;
        for (int i = 0; i < taskSize; i++) {
            Object result = null;
            try {
                result = getQuenueMsgThrow(concurrentExecutor);
            } catch (BusinessException e) {
                logger.warn("BatchContainerExecutor獲取某個任務異常:", e);
                flag = true;
                break;
            }
            results.add(result);
        }
        if (flag) {
            logger.warn("BatchContainerExecutor出現異常,取消本組taskList剩餘任務");
            for (Future<Object> future : futures) {
                try {
                    future.cancel(true);
                } catch (Exception e) {
                    logger.warn("BatchContainerExecutor取消任務異常:", e);
                }
            }
        }
        return results;
    }

    /**
     * 功    能:併發處理taskList中的任務***如果取到第一個想要的結果,則取消剩餘任務
     * 作    者:java瀟邦
     * 時    間:2016-09-01
     */
    public List<Object> executeFirst(List<Callable<Object>> taskList) {
        int taskSize = taskList.size();
        List<Future<Object>> futures = new ArrayList<Future<Object>>(taskSize);
        List<Object> results = new ArrayList<Object>(taskSize);
        try {
            ExecutorCompletionService<Object> concurrentExecutor = new ExecutorCompletionService<Object>(pool);
            for (Callable<Object> callable : taskList) {
                futures.add(concurrentExecutor.submit(callable));
            }
            for (int i = 0; i < taskSize; i++) {
                Object result = getQuenueMsg(concurrentExecutor);
                if (result != null){//假設不爲空是第一個想要數據
                    results.add(result);
                    return results;
                }
            }
        } catch (Exception e) {
            logger.warn("executeFirst:", e);
        } finally {
            for (Future<Object> future : futures) {
                try {
                    future.cancel(true);
                } catch (Exception e) {
                    logger.warn("取消任務:", e);
                }
            }
        }
        return results;
    }

    /**
     * 功    能:獲取任務-超時時間爲10秒
     * 作    者:java瀟邦
     * 時    間:2016-09-01
     */
    private Object getQuenueMsg(ExecutorCompletionService<Object> concurrentExecutor) {
        Object result = null;
        try {
            result = concurrentExecutor.poll(10, TimeUnit.SECONDS).get(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            logger.warn("獲取線程執行結果異常:", e);
            result = e.getMessage();
        }
        return result;
    }

    /**
     * 功    能:獲取任務-超時時間爲10秒
     * 作    者:java瀟邦
     * 時    間:2016-09-01
     */
    private Object getQuenueMsgThrow(ExecutorCompletionService<Object> concurrentExecutor) {
        Object result = null;
        try {
            result = concurrentExecutor.poll(10, TimeUnit.SECONDS).get(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            logger.warn("獲取線程執行結果異常:", e);
            throw new BusinessException("獲取線程執行結果異常");
        }
        return result;
    }

    static class BusinessException extends RuntimeException {
        public BusinessException(Throwable cause) {
            super(cause);
        }

        public BusinessException(String message) {
            super(message);
        }

        public BusinessException(String message, Throwable cause) {
            super(message + cause.getMessage());
        }
    }
}
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

/**
 * 描    述:多線程業務測試
 * 作    者:java瀟邦
 * 時    間:2016-09-01
 */
@Component
public class BusinessMuitThreadService {

    private static final Logger logger = LoggerFactory.getLogger(BusinessMuitThreadService.class);

    @Autowired
    private BatchContainerExecutor batchContainerExecutor;
    @Autowired
    private ThirdService thirdService;

    /**
     * 需求:
     * 1.傳入的barcodeList大於1W條
     * 2.第三方接口,單次最多支持查詢10條記錄
     * 3.多線程併發調用第三方接口
     * 4.由BatchContainerExecutor容器去收集線程
     * 5.照着這個抄變成自己的業務邏輯
     */
    public Map<String, String> getProductMap(final String vendorCode, List<String> barcodeList) {
        List<List<String>> pList = Lists.partition(barcodeList, 10);
        List<Callable<Object>> tasks = new ArrayList<Callable<Object>>(barcodeList.size());
        for (List partition : pList) {
            tasks.add(new MapCallable(vendorCode, barcodeList, thirdService));//添加一個任務
        }
        Map<String, String> map = new HashMap<String, String>(barcodeList.size());
        List<Object> retList = batchContainerExecutor.execute(tasks);
        for (Object obj : retList) {
            if (obj instanceof Map) {
                map.putAll((Map) obj);
            } else {
                logger.warn("非法的數據:{}", obj);
            }
        }
        return map;
    }

    /**
     * 描    述:多線程查詢商品信息,並將每個線程的結果收集起來,返回一個大List
     * 作    者:java瀟邦
     * 時    間:2016-09-01
     */
    public List getProductList(final String vendorCode, List<String> barcodeList) {
        List<List<String>> pList = Lists.partition(barcodeList, 10);
        List<Callable<Object>> tasks = new ArrayList<Callable<Object>>(barcodeList.size());
        for (List partition : pList) {
            tasks.add(new ListCallable(vendorCode, barcodeList, thirdService));//添加一個任務
        }

        List list = new ArrayList(barcodeList.size());
        List<Object> retList = batchContainerExecutor.execute(tasks);
        for (Object obj : retList) {
            if (obj instanceof List) {
                list.addAll((List) obj);
            } else {
                logger.warn("非法的數據:{}", obj);
            }
        }
        return list;
    }

}
import java.util.List;
import java.util.concurrent.Callable;

/**
 * 描    述:獲取任務的線程
 * 作    者:java瀟邦
 * 時    間:2016-09-01
 */
public class MapCallable implements Callable {

    private String vendorCode;

    private List<String> barcodeList;

    private ThirdService thirdService;

    public MapCallable(String vendorCode, List<String> barcodeList, ThirdService thirdService) {
        this.vendorCode = vendorCode;
        this.barcodeList = barcodeList;
        this.thirdService = thirdService;
    }

    @Override
    public Object call() throws Exception {
        return thirdService.getProductMap(vendorCode,barcodeList);
    }
}
import java.util.List;
import java.util.concurrent.Callable;

/**
 * 描    述:獲取任務的線程
 * 作    者:java瀟邦
 * 時    間:2016-09-01
 */
public class ListCallable implements Callable {

    private String vendorCode;

    private List<String> barcodeList;

    private ThirdService thirdService;

    public ListCallable(String vendorCode, List<String> barcodeList, ThirdService thirdService) {
        this.vendorCode = vendorCode;
        this.barcodeList = barcodeList;
        this.thirdService = thirdService;
    }

    @Override
    public Object call() throws Exception {
        return thirdService.getProductList(vendorCode, barcodeList);
    }
}
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 描    述:第三方接口
 * 作    者:java瀟邦
 * 時    間:2016-09-01
 */
@Component
public class ThirdService {


    /**
     * 描    述:最多支持查詢10個條碼
     * 作    者:java瀟邦
     * 時    間:2016-09-01
     */
    public Map<String,String> getProductMap(String vendorCode, List<String> barcodeList){
        Map<String,String> productMap = new HashMap<>();
        productMap.put("sku_001","筆記本");
        productMap.put("sku_002","臺式機");
        return productMap;
    }

    /**
     * 描    述:最多支持查詢10個條碼
     * 作    者:java瀟邦
     * 時    間:2016-09-01
     */
    public List getProductList(String vendorCode, List<String> barcodeList){
        return new ArrayList();
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章