1. 程式人生 > >【精】【多執行緒】ListenableFuture非同步多執行緒查詢實現

【精】【多執行緒】ListenableFuture非同步多執行緒查詢實現

 

業務場景:為優化查詢效率,將原有查詢的條件做成單獨的索引表,每次產生記錄就會同步到索引表中,每次查詢索引表,根據索引便利的條件欄位再分別查詢每張子表的內容,最後封裝成前臺要的實體類。這裡面涉及到非同步查詢,如何保證一條記錄下的子表全部都查出來後才執行下面的操作。

下面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);
		});
	}

       具體的實現就不詳細介紹了,大家可以通過Demo自己測試玩下,再結合展示的部分業務程式碼,就能大概明白使用方法了。有疑問可以留言,也請各位大神不吝賜教!!!