Future vs FutureTask vs ComplatableFuture

发布时间 2023-04-05 21:21:27作者: colorfulworld

1. Future

异步计算结果,提供了一些方法来检验任务是否完成,get()都是阻塞的。

1.1 Future接口方法:

// 取消任务
boolean cancel(boolean mayInterruptIfRunning);
// 判断任务是否已取消  
boolean isCancelled();
// 判断任务是否已结束
boolean isDone();
// 获得任务执行结果
get();
// 获得任务执行结果,支持超时
get(long timeout, TimeUnit unit);

1.2 Future获取任务结果

public Future<?> submit(Runnable task) {  //参数Runnable接口的run()没有返回值,所以submit(Runnable task)返回的Future尽可以用来判断任务是否结束
}
public <T> Future<T> submit(Runnable task, T result) { 
}
public <T> Future<T> submit(Callable<T> task) {
}

注意public<T> Future<T> submit(Runnable task, T result)这个方法,Runnable 接口的实现类 Task 声明了一个有参构造函数 Task(Result r) ,创建 Task 对象的时候传入了 result 对象,这样就能在类 Task 的 run() 方法中对 result 进行各种操作了。result 相当于主线程和子线程之间的桥梁,通过它主子线程可以共享数据。

submit(Callable task):这个方法的参数是一个 Callable 接口,它只有一个 call() 方法,并且这个方法是有返回值的,所以这个方法返回的 Future 对象可以通过调用其 get() 方法来获取任务的执行结果。

 

ExecutorService executorService=Executors.newSingleThreadExecutor();
Future<List<String>> future=executorService.submit(new Callable<List<String>>() {

            @Override
            public List<String> call() throws Exception {
                return callMethod();
                 
            }

        });

 

 

 

2. FutureTask

FutureTask实现了Runnable和Future接口

public class FutureTask<V> implements RunnableFuture<V> {...}

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

由于FutureTask实现Runnable,所以可以作为任务提交给ThreadPoolExecutor,

// 创建FutureTask
FutureTask<Integer> futureTask = new FutureTask<>(()-> 1);
// 创建线程池
ExecutorService es = Executors.newCachedThreadPool();
// 提交FutureTask 
es.submit(futureTask);
// 获取计算结果
Integer result = futureTask.get();

 

由于FutureTask实现Future,所以可以用来获取任务执行结果

public class TestSupplier {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask1=new FutureTask(new CallMongo1("futuretask1"));
        FutureTask futureTask2=new FutureTask(new CallMongo2("futuretask2"));
        Thread t1=new Thread(futureTask1);
        t1.start();
        Thread t2=new Thread(futureTask2);
        t2.start();
        System.out.println("f1 result:"+futureTask1.get());
        System.out.println("f2 result:"+futureTask2.get());
    }

}

@AllArgsConstructor
class CallMongo1 implements Callable<String>{
    String input;
    @Override
    public String call() throws Exception {
        Thread.sleep(2000);
        return input.concat("_callMongo1");
    }
}

@AllArgsConstructor
class CallMongo2 implements Callable<String>{
    String input;
    @Override
    public String call() throws Exception {
        Thread.sleep(3000);
        return input.concat("_callMongo2");
    }
}

 

3. CompletableFuture

CompletableFuture提供了异步函数式编程。可以通过回调方式处理计算结果,并且提供转换和组合CompletableFuture的方法

3.1 CompletableFuture创建方式

a) CompletableFuture < void > runAsync(Runnable runnable){
异步的执行任务,由于是Runnable,所以没有返回值
b) CompletableFuture< void > runAsync(Runnable runnable, Executor executor) {
意思与上诉一致,可以自定义线程池
c) CompletableFuture< U > supplyAsync(Supplier< U > supplier) {
Supplier 接口的 get() 方法是有返回值的。
d) CompletableFuture< U > supplyAsync(Supplier< U > supplier, Executor executor) {
可以自定义线程池

supplyAsync表示创建带返回值的异步任务的,相当于ExecutorService submit(Callable<T> task) 方法,runAsync表示创建无返回值的异步任务,相当于ExecutorService submit(Runnable task)方法,这两方法的效果跟submit是一样的
public class ComplatableFuturedemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorReRunnerService = Executors.newFixedThreadPool(1);
        String date="20230506";
        String name="test";
        CompletableFuture<String> result=CompletableFuture.supplyAsync(()->runAsync(date,name),executorReRunnerService);
        System.out.println(result.get());
    }

    public static String runAsync(String date,String name){
        return date.concat(name);
    }
}

 

3.2 异步回调

thenApply / thenApplyAsync

thenApply 表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中

    public void test5() throws Exception {
        ForkJoinPool pool=new ForkJoinPool();
        // 创建异步执行任务:
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread()+" start job1,time->"+System.currentTimeMillis());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            System.out.println(Thread.currentThread()+" exit job1,time->"+System.currentTimeMillis());
            return 1.2;
        },pool);
        //cf关联的异步任务的返回值作为方法入参,传入到thenApply的方法中
        //thenApply这里实际创建了一个新的CompletableFuture实例
        CompletableFuture<String> cf2=cf.thenApply((result)->{
            System.out.println(Thread.currentThread()+" start job2,time->"+System.currentTimeMillis());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            System.out.println(Thread.currentThread()+" exit job2,time->"+System.currentTimeMillis());
            return "test:"+result;
        });
        System.out.println("main thread start cf.get(),time->"+System.currentTimeMillis());
        //等待子任务执行完成
        System.out.println("run result->"+cf.get());
        System.out.println("main thread start cf2.get(),time->"+System.currentTimeMillis());
        System.out.println("run result->"+cf2.get());
        System.out.println("main thread exit,time->"+System.currentTimeMillis());
    }

Thread[ForkJoinPool-1-worker-1,5,main] start job1,time->1668570856694

main thread start cf.get(),time->1668570856694

Thread[ForkJoinPool-1-worker-1,5,main] exit job1,time->1668570858705

Thread[ForkJoinPool-1-worker-1,5,main] start job2,time->1668570858705

run result->1.2

main thread start cf2.get(),time->1668570858706

Thread[ForkJoinPool-1-worker-1,5,main] exit job2,time->1668570860717

run result->test:1.2

main thread exit,time->1668570860717

 

 

参考文献:

https://blog.csdn.net/weixin_42311088/article/details/114921168

https://blog.csdn.net/jiayoubaobei2/article/details/127879719

 

 

submit(Callable task):这个方法的参数是一个 Callable 接口,它只有一个 call() 方法,并且这个方法是有返回值的,所以这个方法返回的 Future 对象可以通过调用其 get() 方法来获取任务的执行结果。