一、多线程的好处:
唯品会供应链业务场景中,会涉及将多个部门接口聚合查询,一个采购订单创建过程中,可能需要调用外部接口10-600次来组装数据,此时用多线程,就可以缩减接口响应时间。
二、场景:
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();
}
}