前言
Future设计模式是Java多线程开发常用设计模式。一句话,将客户端请求的处理过程从同步改为异步,以便将客户端解放出来,在服务端程序处理期间可以去干点其他事情,最后再来取请求的结果。好处在于整个调用过程中不需要等待,可以充分利用所有的时间片段,提高系统的响应速度。
举个实际生活中例子来说吧,今天小明女朋友过生日,小明去蛋糕店准备给女朋友定个大蛋糕,超级大的那种。
“老板,我要个最大最漂亮的蛋糕”
“好,999元,先付款吧”
“这么贵!!!”想想还是买了吧,掏了钱,付了款,老板给我一张蛋糕票。
“下午过来取。下午拿这张蛋糕票过来拿蛋糕”
“纳尼,这么久”
“没有办法,你可以选择在这等,我们会马上开做,不过我建议你还是去干点别的,去买个花什么的,那样您女朋友会更开心的”
……
下午一点钟我早早的就到蛋糕店了。
“老板,我的蛋糕呢?”
“不好意思,还没做好,您稍微等一会”
“晕”
十分钟后,小明终于拿我的蛋糕票取到了送给女朋友的超级大蛋糕了。
这就是Future模式,蛋糕就是小明要的数据(RealData),去蛋糕店订蛋糕就是我调用取数据的方法,付完钱,我没有真的拿到蛋糕,只是一个蛋糕票(FutureData),要取数据就是拿这个蛋糕票(FutureData)取(get)蛋糕(RealData)。取的时候蛋糕可能还没有做好,你还是要等,如果做好了,你直接拿走就行了。这种模式的好处就是你不用一直在等着蛋糕出来,做蛋糕的时间你可以去做别的事。有一点就是你不能准确的知道多久能做好蛋糕,很有可能来早了,还是要等,但是没有关系,总比一直在等着强
一、自己实现一个Future设计模式
先把每个角色说一下
1、Future:任务结果;其实现类是FutureImpl
2、Task:任务对象;
3、Executor:执行任务的对象;其实实现类是ExecutorImpl
1、任务结果
public interface Future<T> {
//获取任务结果
T get();
//判断任务结果是否完成
boolean done();
}
真正的结果对象如下:
public class FutureImpl<T> implements Future {
//储存真正的结果对象
private T result;
//标志任务是否完成,默认false未未完成
private volatile boolean completeSign = false;
@Override
public synchronized T get() {
while(!completeSign){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return result;
}
@Override
public boolean done() {
return completeSign;
}
public synchronized void set(T result){
completeSign = true;
this.result = result;
this.notifyAll();
}
}
2、任务对象
@FunctionalInterface
public interface Task<T> {
T result();
}
3、执行者对象
public interface Executor {
//提交任务
<T> Future<T> submit(Task<T> task );
//提交任务,任务完成后自动提醒
<T> Future<T> submit(Task<T> task, Consumer<T> out);
}
其实现类如下:
public class ExecutorImpl implements Executor {
@Override
public <T> Future<T> submit(Task<T> task) {
FutureImpl<T> future = new FutureImpl<>();
new Thread(()->{
T result = task.result();
future.set(result);
}).start();
return future;
}
@Override
public <T> Future<T> submit(Task<T> task, Consumer<T> out) {
FutureImpl<T> future = new FutureImpl<>();
new Thread(()->{
T result = task.result();
future.set(result);
out.accept(result);
}).start();
return future;
}
}
测试
Executor executor = new ExecutorImpl();
Future<String> result = executor.submit(FMain::get);
while(!result.done()){
try {
Thread.sleep(1000);
System.out.println("任务1执行中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
···· ···· System.out.println(result.get());
现在我们来看看高并发包中Future设计模式的实现,这是他的类图
二、Future
public interface Future
1、取消任务
//传入true会中断线程停止任务,而传入false则会让线程正常执行至完成,但是会让未执行的任务不执行
boolean cancel(boolean mayInterruptIfRunning)
//判断线程是否被取消(cancel)
//如果任务已经完成,已经被取消或者因为其他原因而无法取消,则false
boolean isCancelled()
疑问,false参数并不会停止任务,那么cancel(false)有什么用呢?
传入false则会让线程正常执行至完成,既然不会中断线程,那么这个cancel方法不就没有意义了吗?
简单来说,传入false参数只能取消还没有开始的任务,若任务已经开始了,就任由其运行下去。
当创建了Future实例,任务可能有以下三种状态:
1、等待状态。此时调用cancel()方法不管传入true还是false都会标记为取消,任务依然保存在任务队列中,但当轮到此任务
运行时会直接跳过。
2、完成状态。此时cancel()不会起任何作用,因为任务已经完成了。
3、运行中。此时传入true会中断正在执行的任务,传入false则不会中断。
2、获取任务结果
会堵塞当前线程,直到获取任务的值
如果计算被取消(调用cancel方法)抛出异常:CancellationException
如果任务中抛出异常,抛出异常:ExecutionException,,我们可以从这个异常中获取错误信息
如果当前线程在等待时中断,抛出异常:InterruptedException
如果获取结果超时,抛出异常:TimeoutException
V get() throws InterruptedException, ExecutionException
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,TimeoutException
3、判断任务是否完成
//如果任务已完成返回true
//完成:可能是由于正常终止,异常或取消 - 在所有这些情况下,此方法将返回true
boolean isDone()
三、CompletableFuture
public class CompletableFuture extends Object implements Future, CompletionStage
1、CompletableFuture的创建
默认使用ForkJoinPool.commonPool()作为它的线程池执行异步代码
//创建一个给定初始值的completedFuture
static <U> CompletableFuture<U> completedFuture(U value)
//只要异步线程队列有一个任务率先完成就返回,这个特性可以用来获取最快的那个线程结果
static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
//runAsync 和 supplyAsync 方法的区别是runAsync返回的CompletableFuture是没有返回值的
static CompletableFuture<Void> runAsync(Runnable runnable)
static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
completedFuture:
allOf:在这里我们可以将对各future实例添加到allOf方法中,然后通过future的get()方法获取future的状态。如果allOf里面的所有线程为执行完毕,主线程会阻塞,直到allOf里面的所有线程都执行,线程就会被唤醒。
2、上游线程2个 ,用其中的一个线程值作为下游线程入参
他们的区别在于一个是有返回值的,而另一个没有返回值
<U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T,U> fn)
<U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn)
<U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn, Executor executor)
CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor)
这里我们就拿acceptEither做案例
CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务A开始:"+Thread.currentThread().getName());
SleepUtil.sleep(1);
System.out.println("任务A结束");
return "我是任务A";
});
CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务B开始:" + Thread.currentThread().getName());
SleepUtil.sleep(3);
System.out.println("任务B结束");
return "我是任务B";
}
);
completableFuture1.acceptEither(completableFuture2,System.out::println);
acceptEither的结果是输出:我是任务A
如果把任务A睡10秒,那么
acceptEither的结果是输出:我是任务B
3、上游线程是2个(独立的),使用他们的线程值作为下游线程入参
他们的区别在于一个是有返回值的,而另一个没有返回值
<U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
<U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
<U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor)
<U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action)
<U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action)
<U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action, Executor executor)
这里我们就拿thenAcceptBoth做案例
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1开始:"+Thread.currentThread().getName());
SleepUtil.sleep(3);
System.out.println("任务1结束");
return 1;
});
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2开始:"+Thread.currentThread().getName());
SleepUtil.sleep(3);
System.out.println("任务2结束");
return 1;
});
completableFuture1.thenAcceptBoth(completableFuture2,(a,b)-> System.out.println(a+b));
4、处理上游线程结果
//返回值是一个CompletableFuture
<U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
<U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn)
<U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn, Executor executor)
//返回值是一个object
<U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
//没有返回值
CompletableFuture<Void> thenAccept(Consumer<? super T> action)
CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
//可以处理异常,返回值是一个object
CompletableFuture<T> whenComplete( BiConsumer<? super T,? super Throwable> action)
CompletableFuture<T> whenCompleteAsync( BiConsumer<? super T,? super Throwable> action)
CompletableFuture<T> whenCompleteAsync( BiConsumer<? super T,? super Throwable> action, Executor executor)
5、处理上游线程异常
handle 方法和 thenApply 方法处理方式基本一样。不同的是 handle 是在任务完成后再执行,还可以处理异常的任务。thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。
//异常处理,不管任务是否出现,都会执行,所以如果当前handle中出现异常,会堵塞后面的任务
<U> CompletableFuture<U> handle( BiFunction<? super T,Throwable,? extends U> fn)
<U> CompletableFuture<U> handleAsync( BiFunction<? super T,Throwable,? extends U> fn)
<U> CompletableFuture<U> handleAsync( BiFunction<? super T,Throwable,? extends U> fn, Executor executor)
//只有在异常出现时才会执行
CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
//判断任务是否以异常方式完成
//如果此CompletableFuture以任何方式完成异常完成,则返回true
boolean isCompletedExceptionally()
案例
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> completableFuture2 = completableFuture1.thenCompose(a->CompletableFuture.supplyAsync(() -> 1 + a);
System.out.println(completableFuture2.join()); //2
6、控制线程的顺序执行
线程直接不存在通信
//A线程运行玩后,执行结束C,action是C
CompletableFuture<Void> thenRun(Runnable action)
CompletableFuture<Void> thenRunAsync(Runnable action)
CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)
//A和B其中一个线程运行结束后,执行线程C,action是C
CompletableFuture<Void> runAfterBoth( CompletionStage<?> other, Runnable action)
CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action)
CompletableFuture<Void> runAfterBothAsync( CompletionStage<?> other, Runnable action, Executor executor)
//A和B线程都运行结束后,执行线程C,action是C
CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action)
CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action)
CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor)
7、设置任务结果
1、软设置,只有在任务未完成情况下才能修改成功
//设置get()种相关方法的值,不会终止任务
boolean complete(T value)
//终止任务,调用 get()和相关方法抛出给定的异常
boolean completeExceptionally(Throwable ex)
2、强设置,无论任务是否完成,都能修改成功
//设置get()种相关方法的值,不会终止任务
void obtrudeValue(T value)
//终止任务,调用 get()和相关方法抛出给定的异常
void obtrudeException(Throwable ex)
8、获取信息
//会堵塞,会抛出异常,返回任务结果值
T get() throws InterruptedException, ExecutionException
//会堵塞,会抛出异常,返回任务结果值
T get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException,TimeoutException
//如果已完成,则返回结果值,否则返回IfAbsent
T getNow(T valueIfAbsent)
//会堵塞,返回任务结果值
T join()
//返回完成等待完成此CompletableFuture的CompletableFutures的估计数
int getNumberOfDependents()