最近研究了一下异步调用,接下来几篇博客是跟异步调用相关的,首先使用@FunctionalInterface接口实现一个简单的步调用,也就是本篇博客主要内容。
然后再加上ZMQ,实现一个带网络通信的异步调用。再下一步就是复杂一点的RPC调用,最终目的是实现一个使用ZMQ的分布式调用系统。
Flag已经立,目标也定好了,先从简单的说起。
情景假设:假设一个程序需求是用户Person查看用户密码,这个需要访问数据库,可能是耗时操作,把这个做成异步调用。
1 @FunctionalInterface介绍:
FunctionalInterface的接口被称为函数式接口,该接口只能有一个自定义方法,但是可以包括从object类继承而来的方法。如果一个接口只有一个方 法,则编译器会认为这就是一个函数式接口
定义一个@FunctionalInterface接口:
package com.zjq.java8.methodInterface;
@FunctionalInterface
public interface GofFunction<T1,T2> {
public void execute(T1 t1,T2 t2);
}
2 定义一个参数包装类,可以包装任何参数:
package com.zjq.java8.methodInterface;
import java.util.HashMap;
import java.util.Map;
public class ParamContext {
private Map<String,Object> datas=new HashMap<String,Object>();
public ParamContext(Object...params){
if(params==null||params.length==0){
return;
}
for(int i=0;i<params.length;){
datas.put((String) params[i], params[i+1]);
i+=2;
}
}
@SuppressWarnings("unchecked")
public <R> R get(String key){
return (R)datas.get(key);
}
}
3 定义一个监听类,里面有两个map,一个是所调用方法的map,一个是对应参数的map,map的key值是生成的uuid
package com.zjq.java8.methodInterface;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class ListenCall {
ConcurrentHashMap<String,GofFunction<ParamContext, ParamContext>> methodMap=new ConcurrentHashMap<String,GofFunction<ParamContext, ParamContext>>();
ConcurrentHashMap<String,ParamContext> paramMap=new ConcurrentHashMap<String,ParamContext>();
/**
* 获取请求编号
* @return
*/
public String getCallId(){
UUID uuid = UUID.randomUUID();
return uuid.toString();
}
/**
* 监听返回值
* @param method
* @param callId
* @param context
*/
public void listenResult(GofFunction<ParamContext, ParamContext> method,String callId,ParamContext context) {
methodMap.put(callId, method);
paramMap.put(callId, context);
}
/**
* 等待处理结果
* @param result
* @param callId
*/
public void waitForResult(ParamContext result,String callId){
GofFunction<ParamContext, ParamContext> funtion=methodMap.get(callId);
if(funtion!=null){
ParamContext context=paramMap.get(callId);
if(context==null){
context=new ParamContext();
}
funtion.execute(result,context);
}
}
}
4 定义person的service类,里面有一些逻辑方法,有一个模拟读取数据库的方法,此方法是多线程的,模拟读取数据库,线程休眠4秒。
package com.zjq.java8.methodInterface;
public class PersonService {
private ListenCall listenCall=new ListenCall();
/**
* 这里模拟根据用户名从数据库查询密码
* @param name 用户名
* @param callId 请求调用的id
*/
public void getPwdFromDb(String name,String callId){
new Thread(new Runnable() {
@Override
public void run() {
String sql="select from person where name="+"'"+name+"'";
String pwd="1111";
try {
//这里等待4秒,模拟毒区数据库的书剑,时间有点夸张
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitForResult(new ParamContext("pwd",pwd),callId);
}
}).start();
}
public String getCallId(){
return listenCall.getCallId();
}
public void waitForResult(ParamContext p,String callId){
listenCall.waitForResult(p, callId);
}
public void listenResult(GofFunction<ParamContext, ParamContext> method,String callId,ParamContext context){
listenCall.listenResult(method, callId, context);
}
}
5 用户的管理类。
package com.zjq.java8.methodInterface;
public class PersonManager {
PersonService personService=new PersonService();
public static void main(String[] args) {
PersonManager callTest=new PersonManager();
callTest.seePwd();
}
/**
* 假设用户想查看一下密码
*/
private void seePwd(){
String name="1111";
String time=String.valueOf(System.currentTimeMillis());
String callId=personService.getCallId();
personService.getPwdFromDb(name,callId);
personService.listenResult(this::getPwd,callId,new ParamContext("time",time));
System.out.println("数据库读取数据,可能有点耗时,这里做成异步操作,我先做其他事情。。。。。。。。。");
}
/**
* 获取到了密码
* @param result
* @param context
*/
private void getPwd(ParamContext result,ParamContext context){
String pwd=result.get("pwd");
long sed=(System.currentTimeMillis()-Long.valueOf(context.get("time")))/1000;
System.out.println("经过"+sed+"秒"+"查询用户密码的调用终于返回了");
System.out.println("得到密码:"+pwd);
}
}
6 程序执行结果:
数据库读取数据,可能有点耗时,这里做成异步操作,我先做其他事情。。。。。。。。。
经过4秒查询用户密码的调用终于返回了
得到密码:1111
分析,程序先打印的第一句,等4秒后才打印后面的,说明异步调用成功。