业务场景:为优化查询效率,将原有查询的条件做成单独的索引表,每次产生记录就会同步到索引表中,每次查询索引表,根据索引便利的条件字段再分别查询每张子表的内容,最后封装成前台要的实体类。这里面涉及到异步查询,如何保证一条记录下的子表全部都查出来后才执行下面的操作。
下面Demo简单演示下操作步骤:
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import org.junit.Test;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.ymdd.galaxy.report.utils.BeanCopierUtils;
public class ThredPoolTest {
private static ListeningExecutorService pool;
static {
//通过guava创建固定容量的线程池,用完需要调用shutdown方法关闭线程池。
pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
}
@Test
public void test() throws InterruptedException, ExecutionException {
System.out.println("******用ListenableFuture异步操作******");
Instant now = Instant.now();
Student student = new Student("元歌", "男", 12, "刺客");
Person person = new Person();
//将student和person相同的字段的值赋给person
BeanCopierUtils.copyProperties(person, student);
List<Student> studentList = new ArrayList<Student>();
List<Person> personList = new ArrayList<Person>();
ListenableFuture<List<Student>> studentFuture = pool.submit(() -> {
System.out.println("studentList 线程开始");
//这里使用了lambda表达式,
for (int i = 0; i < 100000000; i++) {
//也可以直接通过匿名内部类实现callable,runnable区别,一个有返回值,一个没有返回值
studentList.add(student);
}
System.out.println("studentList 线程结束");
return studentList;
});
ListenableFuture<List<Person>> personFuture = pool.submit(() -> {
System.out.println("personList 线程开始");
for (int i = 0; i < 100000000; i++) {
personList.add(person);
}
System.out.println("personList 线程结束");
return personList;
});
List<Student> sList = studentFuture.get();
System.out.println("studentList获取");
List<Person> pList = personFuture.get();
System.out.println("personList获取");
pool.shutdown();//用完之后关闭线程池
Instant now1 = Instant.now();
System.out.println(sList.size());
System.out.println(pList.size());
System.out.println("使用线程池耗时:"+Duration.between(now, now1).toMillis());
}
@Test
public void test1() throws ExecutionException, InterruptedException {
System.out.println("******用普通For循环操作******");
Instant now = Instant.now();
Student student = new Student("司马懿", "男", 16, "刺客、法师");
Person person = new Person();
//将student和person相同的字段的值赋给person
BeanCopierUtils.copyProperties(person, student);
List<Student> studentList = new ArrayList<Student>();
List<Person> personList = new ArrayList<Person>();
for (int i = 0; i < 100000000; i++) {
studentList.add(student);
}
for (int i = 0; i < 100000000; i++) {
personList.add(person);
}
Instant now1 = Instant.now();
System.out.println(studentList.size());
System.out.println(personList.size());
System.out.println("不使用线程池耗时:"+Duration.between(now, now1).toMillis());
}
}
用到的工具和类:
Student、Preson类:
public class Student {
private String name;
private String sex;
private Integer age;
private String remark;
public Student(String name,String sex,Integer age,String remark) {
this.name=name;
this.sex=sex;
this.age=age;
this.remark=remark;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
public class Person {
private String name;
private String sex;
private Integer age;
private String remark;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
BeanCopierUtils工具类(Bean属性转换)
import java.util.HashMap;
import java.util.Map;
import net.sf.cglib.beans.BeanCopier;
public class BeanCopierUtils {
public static Map<String, BeanCopier> beanCopierMap = new HashMap<String, BeanCopier>();
/**
* @Title: copyProperties
* @Description: TODO(bean属性转换)
* @param source 资源类
* @param target 目标类
* @author 050083
* @date 2018年8月25日下午4:56:44
*/
public static void copyProperties(Object source,Object target){
String beanKey = generateKey(source.getClass(),target.getClass());
BeanCopier copier = null;
if (!beanCopierMap.containsKey(beanKey)) {
copier = BeanCopier.create(source.getClass(), target.getClass(), false);
beanCopierMap.put(beanKey, copier);
}else {
copier = beanCopierMap.get(beanKey);
}
copier.copy(source, target, null);
}
private static String generateKey(Class<?>class1,Class<?>class2){
return class1.toString() + class2.toString();
}
}
执行结果如下:
对比结果不难发现ListenableFuture异步多线程操作要比普通的操作上不少。
下面给大家看一下我实际项目中的应用(多表查询的),代码如下:
// (1)数据库中单独查表
ListenableFuture<List<Order>> orderFuture = queryOrderListFuture(orderNos);
// 获取地址数据
ListenableFuture<List<OrderAddress>> addressFuture=queryOrderAddressListFuture(orderNos);
// 获取受理表数据
ListenableFuture<List<OrderAccept>> orderAcceptFuture=queryOrderAcceptListFuture(orderNos);
// 获取包装数据
ListenableFuture<List<OrderPackageRule>> orderPackageRuleFuture = queryOrderPackageRuleListFuture(orderNos);
// 获取订单服务费用表
ListenableFuture<List<OrderServiceFee>> orderServiceFeeFuture = queryOrderServiceFeeListFuture(orderNos);
// 获取优惠券表数据
ListenableFuture<List<OrderDiscount>> orderDiscountFuture = queryOrderDiscountFuture(orderNos);
// 保证所有查询执行完毕后才执行下面的业务代码
// (2)数据进行处理
List<OrderDetail> orderDetailList = null;
try {
// 订单转换orderDetail
orderDetailList = queryOrderData(null != orderFuture ? orderFuture.get() : null);
// 获取地址数据
queryOrderAddressData(null != addressFuture ? addressFuture.get() : null, orderDetailList);
// 获取受理表数据
queryOrderAcceptData(null != orderAcceptFuture.get() ? orderAcceptFuture.get() : null, orderDetailList);
// 获取包装数据
queryOrderPackageRuleData(null != orderPackageRuleFuture.get() ? orderPackageRuleFuture.get() : null,orderDetailList);
// 获取订单服务费用表
queryOrderServiceFeeData(null != orderServiceFeeFuture.get() ? orderServiceFeeFuture.get() : null,orderDetailList);
// 获取优惠券表数据
getOrderDiscountData(null != orderDiscountFuture.get() ? orderDiscountFuture.get() : null,orderDetailList);
// LOGGER.info("查询订单处理完====六个表====后的数据:{}",JSON.toJSON(orderDetailList));
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
用到的方法简单展示一个(其余类似):
/***
* 通过订单号,获取订单信息
*
* @param orderNos
* @return
*/
@Override
public List<Order> queryOrderListByOrderNo(List<Long> orderNos) {
OrderExample example = new OrderExample();
com.ymdd.galaxy.order.entity.ord.model.OrderExample.Criteria createCriteria = example.createCriteria();
if (CollectionUtils.isNotEmpty(orderNos) && orderNos.size() == 1) {
createCriteria.andOrderNoEqualTo(orderNos.get(0));
} else {
createCriteria.andOrderNoIn(orderNos);
}
List<Order> orderList = this.selectByExample(example);
return orderList;
}
/**
* 获取订单信息的ListenableFuture
*/
private ListenableFuture<List<Order>> queryOrderListFuture(List<Long> orderNos) {
return pool.submit(() -> {
return queryOrderListByOrderNo(orderNos);
});
}