java高并发基础篇之多线程Future设计模式

前言

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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值