【精】【多執行緒】ListenableFuture非同步多執行緒查詢實現
阿新 • • 發佈:2018-11-02
業務場景:為優化查詢效率,將原有查詢的條件做成單獨的索引表,每次產生記錄就會同步到索引表中,每次查詢索引表,根據索引便利的條件欄位再分別查詢每張子表的內容,最後封裝成前臺要的實體類。這裡面涉及到非同步查詢,如何保證一條記錄下的子表全部都查出來後才執行下面的操作。
下面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);
});
}