一、多線程的好處:
接口調用,往往離不開多線程,可以減少響應時間
二、場景:
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();
}
}