在业务处理过程中,我们会经常遇到一次用户请求往往会涉及到多个操作。以获取用户信息及其账户余额为例,它包含了从用户中心获取用户基本信息(T1)以及从用户金融中心获取其对应的账户余额信息(T2)
如果采用同步调用的话,总的响应时间将是 T1 + T2。
类似这种两个子操作在执行过程中没有依赖关系的调用切忌使用同步,这样会大大降低系统的并发量。
下面我们介绍几种使用异步调用来来优化掉同步调用,从而缩短响应时间为max(T1,T2)
FutureTask
FutureTask可用于异步获取执行结果或取消执行任务的场景。FutureTask 实现了 RunnableFuture 接口(继承了Future 和 Runnable 接口)。
提供isDone操作可以查询计算是否已经完成;
提供get操作获取计算的结果,结果只可以在计算完成之后获取,当计算没有完成的时候get方法会阻塞;
提供cancel操作取消执行异步计算;
一个FutureTask 可以用来包装一个 Callable 或是一个runnable对象。因为FurtureTask实现了Runnable方法,所以一个 FutureTask可以提给一个Excutor或线程执行.
下面以FutureTask 来优化刚开始的同步操作:
public static void testGetUserInfo() {
FutureTask task1 = new FutureTask(new Callable<JSONObject>() {
@Override
public JSONObject call() throws Exception {
try {
Thread.sleep(1000L);
} catch (Exception ex) {
}
return JSONObject.parseObject("{\"id\": \"1234567890\",\n" +
" \"account\": \"10000\"}");
}
});
FutureTask task2 = new FutureTask(new Callable<JSONObject>() {
@Override
public JSONObject call() throws Exception {
try {
Thread.sleep(2000L);
} catch (Exception ex) {
}
return JSONObject.parseObject("{\"id\": \"1234567890\",\n" +
"\"username\": \"ZhangSan\"," +
"\"age\": \"28\"," +
"\"address\": \"BeiJing\"" +
"}");
}
});
new Thread(task1).start();
new Thread(task2).start();
try {
System.out.println(task1.get());
System.out.println(task2.get());
} catch (Exception ex) {
ex.printStackTrace();
}
}
CompletableFuture
Future 接口的局限性**
通过上面的例子,我们知道Future接口提供了方法获取计算的结果。但是这些特性还不足以让你编写简洁的并发代码。比如,我们很难表述Future结果之间的依赖性;从文字描述上这很简单,“当长时间计算任务完成时,请将该计算的结果通知到另一个长时间运行的计算任务,这两个计算任务都完成后,将计算的结果与另一个查询操作结果合并”。在此,通过CompletableFuture(它实现了Future和CompletionStage接口)以更直观的方式将上述需求都变为可能。
应对Future的完成事件(即当Future的完成事件发生时会收到通知,并能使用Future 计算的结果进行下一步的操作,不只是简单地阻塞等待操作的结果)
CompletableFuture.supplyAsync(() -> {
System.out.println("write translog");
return true;
}).thenApplyAsync(b -> {
System.out.print("end");
if (b) return "succes";
return "fail";
});
等待Future集合中快结束的任务完成,并通过它的结果执行不同操作
有这样一个任务:
T由N个子任务构成,每个子任务完成的时长各不同。若其中有一个子任务失败,所有任务结束,T任务失败。
enum Result {SUCCESS, FAIL, CANCELLED}
static List<MyTask> tasks = new ArrayList<>();
public static void main(String[] args) throws Exception {
MyTask task1 = new MyTask("task1", 3, Result.SUCCESS);
MyTask task2 = new MyTask("task2", 4, Result.SUCCESS);
MyTask task3 = new MyTask("task3", 1, Result.FAIL);
tasks.add(task1);
tasks.add(task2);
tasks.add(task3);
for (MyTask task : tasks) {
CompletableFuture f = CompletableFuture.supplyAsync(() -> task.run()).thenAccept(result -> callback(result, task));
}
System.in.read();
}
private static void callback(Result result, MyTask myTask) {
//这里要考虑周到,除非cancel五状态,幂等,否则应该加锁同步
if (result == Result.FAIL) {
for (MyTask task : tasks) {
if (task != myTask) task.cancel();
}
}
//处理流程结束
//通知其他线程结束回滚
//超时处理
}
static class MyTask {
String name;
int timemills;
Result ret;
volatile boolean cancelling = false;
volatile boolean cancelled = false;
public MyTask(String name, int timeInSeconds, Result ret) {
this.name = name;
this.timemills = timeInSeconds * 10000;
this.ret = ret;
}
public Result run() {
int interval = 100;
int total = 0;
try {
for(;;) {
Thread.sleep(interval);
total += interval;
if (total >= timemills) break;
if (cancelled) return Result.CANCELLED;
}
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println(name + " is ended!");
return ret;
}
public void cancel() {
if (cancelled) return ;
synchronized (this) {
if (cancelled) return;
cancelling = true;
System.out.println(name + " is cancelling");
try {
TimeUnit.MICROSECONDS.sleep(50);
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println(name + " is cancelled");
cancelled = true;
}
}
}
等待Future集合中的所有任务都完成
CompletableFuture.allOf(
CompletableFuture.supplyAsync(() -> {
System.out.println("write translog");
//writeTranslog();
return CompletableFuture.completedFuture(true);
}),
CompletableFuture.supplyAsync(() -> {
System.out.println("write to database");
//writeMysql();
return CompletableFuture.completedFuture(true);
})
)
.whenComplete( (v, ex ) -> {
if( ex == null ) {
logger.info( "Succeed to save record");
} else {
logger.error("Fail to save record {}", ex );
}
});
将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第 一个的结果。
CompletableFuture.supplyAsync(() -> {
System.out.println("write translog");
try {
Thread.sleep(5000);
}catch (Exception ex) {
}
System.out.println("write translog end");
return true;
})
.thenCombine(CompletableFuture.supplyAsync(() -> {
System.out.println("write to database");
try {
Thread.sleep(10000);
}catch (Exception ex) {
}
System.out.println("write to database end");
return false;
}), (b1, b2 ) -> b1 != null && b2 != null && b1 && b2)
.thenCombine(CompletableFuture.supplyAsync(() -> {
System.out.println("write to syslog");
try {
Thread.sleep(5000);
}catch (Exception ex) {
}
System.out.println("write to syslog end");
return true;
}), (b1, b2 ) -> b1 != null && b2 != null && b1 && b2)
.whenComplete((b, ex) -> {
if( !b || ex != null) {
System.out.print("Fail to save record ");
} else {
System.out.print("Success to save record ");
}
});
try {
Thread.sleep(20000);
}catch (Exception ex) {
}