环境
java:1.7
前言
最近又遇到了,需要去多张表中拿数据,大概5个表;
一个表对应一个方法,线性调用的话,大概需要9秒钟。
之前我也总结过 使用CountDownLatch
,但是那个没有写好,写的有点乱。
需求
我需要从6张表中拿数据;
1、先去一张表中拿到符合条件的股票代码集合
2、在用这个股票代码集合去查询另外5张表。
3、需要获取返回值
技术方案
之前我仅仅只用了CountDownLatch
和Executors.newFixedThreadPool()
返回值,我是通过定义Map
变量来获取的;这样做我就不得不有如下代码:
Map<String, Map<String, Object>> stockTagData = new HashMap();
Map<String, Object> pe = new HashMap();
Map<String, Object> historyPe = new HashMap();
Map<String, Object> pb = new HashMap();
Map<String, Object> historyPb = new HashMap();
然后再设置进去。这样做代码不美观,多了的话,给人一种乱乱的感觉。
这次使用CountDownLatch
、Executors.newFixedThreadPool()
、FutureTask
和Callable
来实现。
CountDownLatch
是并发的辅助方法,用来我等待的方法都执行完了。
Executors.newFixedThreadPool()
线程池。
FutureTask
用来获取方法返回的值。
Callable
:有返回值就用它,无返回值就用Runnable
中的run
方法。
代码
类名:HitStocksThread
/**
*
*
* @version 1.0
* @since JDK1.7
* @author yutao
* @company
* @copyright (c) 2016 SunTime Co'Ltd Inc.All rights reserved.
* @date 2018年12月4日上午11:34:43
*/
public class HitStocksThread implements Callable<Map<String, Map<String, Object>>> {
private CountDownLatch countDownLatch;
private String methodName;
private Collection<String> stocks;
private Date date;
private Set<String> codeSet;
public HitStocksThread(CountDownLatch countDownLatch, String methodName) {
this.countDownLatch = countDownLatch;
this.methodName = methodName;
}
public Collection<String> getStocks() {
return stocks;
}
public void setStocks(Collection<String> stocks) {
this.stocks = stocks;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Set<String> getCodeSet() {
return codeSet;
}
public void setCodeSet(Set<String> codeSet) {
this.codeSet = codeSet;
}
/**
*
* @see java.util.concurrent.Callable#call()
*/
@Override
public Map<String, Map<String, Object>> call() throws Exception {
Map<String, Map<String, Object>> result = null;
switch (methodName) {
case "event":
result = TEventAllApiDao.GetStocksList(stocks, date, codeSet);
break;
case "nine":
result = GgStockNineTradeStatisticsDao.getStocksColl(stocks, date, codeSet);
break;
case "multi":
result = GgStockMultiSpaceStatisticsDao.getStocksColl(stocks, date, codeSet);
break;
case "hlht":
result = TeventPerformanceMarkDao.getStocksColl(stocks, date, codeSet);
break;
case "tags":
result = GgMystockTags1.getTagsMap(stocks);
break;
default:
break;
}
countDownLatch.countDown();
return result;
}
}
这个类用于创建子线程,其根据methodName
来判断,需要执行哪个方法。
类名:HitStocksTask
/**
*
*
* @version 1.0
* @since JDK1.7
* @author yutao
* @company
* @copyright (c) 2016 SunTime Co'Ltd Inc.All rights reserved.
* @date 2018年12月4日下午3:00:01
*/
public class HitStocksTask extends FutureTask<Map<String, Map<String, Object>>> {
private Map<String, Map<String, Object>> result;
private String name;
/**
* @param callable
*/
public HitStocksTask(HitStocksThread callable, String name) {
super(callable);
this.name = name;
}
@Override
protected void done() {
super.done();
try {
result = this.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
public Map<String, Map<String, Object>> getResult() {
return result;
}
public String getName() {
return name;
}
}
这个类继承于FutureTask
,是Future
的实现类。
之所以不使用Future
,因为使用其get()
方法时,会阻塞线程。
也就是如果在主线程中使用get()
方法,就会把主线程给阻塞了。
所以使用FutureTask
,在其重载的done
方法中来使用get()
,这样阻塞的就是子线程自己。
类名: HitStocksService
具体的应用:
/**
* @return
* @author yutao
* @param rows
* @param page
* @date 2018年12月3日上午11:26:02
*/
public static List<Map<String, Object>> GetHitStocksList(int page, int rows, String clientIp) {
//获取近三个交易日的数据
List<Date> dateList = GGDate.getLastExchangeDatas(DateUtil.getYesterday(), 3);
//获取评分大于等于50分的股票代码
Set<String> stocks = TApiStockScoreNewDao.getStocksScoreColl(dateList.get(0));
Date date = dateList.get(dateList.size()-1);
Set<String> codeSet = new HashSet<>();
//初始化 需要执行的方法
List<String> initMethodName = initMethodName();
int size = initMethodName.size();
//==========================================
CountDownLatch countDownLatch = new CountDownLatch(size);
Map<String, Map<String, Object>> tagsMap = null;
List<HitStocksTask> futureList = new ArrayList<>();
for(int i=0; i<size; i++) {
String name = initMethodName.get(i);
HitStocksThread hitStocksThread = new HitStocksThread(countDownLatch, name);
hitStocksThread.setCodeSet(codeSet);
hitStocksThread.setDate(date);
hitStocksThread.setStocks(stocks);
HitStocksTask futureTask = new HitStocksTask(hitStocksThread, name);
ExectorThreadPool.newFixedThreadPool.submit(futureTask);
futureList.add(futureTask);
}
result = new ArrayList<>();
try {
countDownLatch.await(5000, TimeUnit.MILLISECONDS);
//下面回到主线程去获取子线程执行的结果
for(HitStocksTask future : futureList) {
Map<String, Map<String, Object>> reMap = future.getResult();
if(reMap != null) {
Collection<Map<String,Object>> values = reMap.values();
String name = future.getName();
if("tags".equals(name)) {
tagsMap = reMap;
continue;
}
if(values != null && !values.isEmpty()) {
result.addAll(values);
}
}
}
Map<String, String> apiParams = new HashMap<String, String>();
String codekey = StkPoolCacheUtil.cacheStockPool(new ArrayList<String>(codeSet));
apiParams.put("con_key", codekey);
apiParams.put("get_fields", "quantity_ratio;price_change_rate;name;price");
Map<String, Object> plateHqs = GoGoalApi.getMap(ApiCenter.V1_FT_HQ_SNAPSHOT_GET_MAP, apiParams, clientIp);
GGLogger.warn("---GetHitStocksList--hangqing--" + plateHqs);
for(Map<String, Object> mm : result) {
String codeStr = mm.get("stock_code").toString();
if(tagsMap != null) {
Map<String, Object> tagMM = tagsMap.get(codeStr);
if(tagMM != null) {
mm.putAll(tagMM);
}
}
String fullcode = HQBaseService.convertFullCode(codeStr);
if(plateHqs == null) {
continue;
}
Map<String, Object> map = (Map<String, Object>)plateHqs.get(fullcode);
if(map == null) {
continue;
}
//量比
mm.put("quantity_ratio", map.get("quantity_ratio"));
mm.put("price", map.get("price"));
mm.put("price_change_rate", map.get("price_change_rate"));
}
ListSortPageUtils.resultOrder(result, "quantity_ratio", -1);
result = ListSortPageUtils.limit(result, 1, 30);
GGCacheHelper.setex(null, result, 60*5, "aihit", params);
result = ListSortPageUtils.limit(result, page, rows);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
/**
*
* @author yutao
* @return
* @date 2018年12月4日上午11:54:26
*/
private static List<String> initMethodName() {
List<String> name = new ArrayList<String>() {
{
add("event");
add("nine");
add("multi");
add("hlht");
add("tags");
}
};
return name;
}
上面这个类 是程序的入口,当然我是从项目里抽出来的,所以没有main
方法。
总结
1、当并发编程的方法需要有返回结果时,实现Callable<>
接口;
2、获取子线程的结果,可以继承FutureTask<>
,再重写done
方法。
3、CountDownLatch
我这里用于主线程去等待子线程执行完毕。
4、上面的写法应该类似于策略模式。
我上面的方法,里面很多都是公司自己封装的方法。请根据情况自己删除。