Future

我们上一篇文章《线程池原理》主要学习了线程池的参数和提交任务的方法execute,本篇文章和大家一起学习提交任务的另一个方法submit。

在开发中经常会遇到一个任务的执行时间比较长,如果我们一直等待任务的返回结果将会使线程进入长时间的阻塞,JDK1.5之后引入了Future,并且在JDK1.8时引入了CompletableFuture解决了此问题。

Future介绍

Future代表着一个异步任务在未来的执行结果,这个结果可以在最终的某个时间通过Future提供的get方法获得,对于一个时间比较长的执行任务来说,用Future是比较合适的,任务在执行过程中线程可以先去做其他事情。

我们先来看看Future接口中定义了哪些方法:

public interface Future<V> {
 //取消异步的任务
    //如果mayInterruptIfRunning为true,表示可以打断正在运行的线程,则工作线程将会被打断
    //如果mayInterruptIfRunning为false,正在运行的线程将不受影响继续执行任务
    boolean cancel(boolean mayInterruptIfRunning);
    
 //判断异步任务是否被取消
    //不论mayInterruptIfRunning为true还是false,只要执行了cancel方法,isCancelled返回的结果都为true
    boolean isCancelled();

    //判断异步任务是否执行结束    
    boolean isDone();
    
    //获取异步任务的执行结果
    //如果任务还未执行结束,该方法会使当前线程阻塞
    //如果任务运行错误,该方法会抛出ExecutionException异常
    V get() throws InterruptedException, ExecutionException;

   //获取异步任务的执行结果,可以设置线程阻塞的时间
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

ExecutorService与Future

线程池可以通过submit方法提交一个Callable类型的任务并且返回Future,而且还可以提交一个Runnable类型的任务。

//提交Callable类型的任务,在任务执行结束后可以通过get方法获取到结果
public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
   }

//构建FutureTask
 protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
     return new FutureTask<T>(callable);
 }
//提交Runnable类型的任务并且返回Future,在执行执行结束后,通过get方法获取的结果为null
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
//提交Runnable类型的任务并且返回Future,在执行执行结束后,通过get方法获取不到结果,通过submit方法则可以
public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}
//两个方法创建任务的方式都是new FutureTask
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

FutureTask是Future的一个实现类,也是使用比较多的一个实现类,它除了实现Future接口中定义的get和done方法之外,还额外增加了finish方法。

我们通过一个例子熟悉下submit方法的用法:

public static void main(String[] args) {
    ExecutorService execute = Executors.newFixedThreadPool(10);
    Future<String> future1 = execute.submit(()->{
        TimeUnit.SECONDS.sleep(1);
        return "Hello JavaCodeGirl";
    });
    System.out.println("future1任务执行结果为:" + future1.get());
    Future<String> future2 = execute.submit(()->{
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("提交一个Runnable类型的任务,不接收结果");
    }, null);
    System.out.println("future2任务执行结果为:" + future2.get());

    AtomicInteger result = new AtomicInteger();
    Future<AtomicInteger> future3 = execute.submit(()->{
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        result.set(100);
    }, result);
    System.out.println("future3任务执行结果为:" + future3.get());

    execute.shutdown();

}

通过测试代码我们可以发现Future它无法被动的去接收任务,它必须是我们程序员通过get方法去获取计算的结果,但是我们又很难知道任务在哪个时间节点能执行结束,而且任务抛出的异常信息也只能在get方法获取结果时才能知道。

上述代码我们使用了3个Future,他们之间是相互独立的,我们并不能获取到future1的结果将其应用到future2中。

所以开发中我们不常直接使用Future,而是使用JDK1.8引入的新Future:CompletableFuture。

CompletableFuture介绍

CompletableFuture类实现了Future接口,所以它也是一个Future,在开发中我们也可以把它当作Future来使用。但是它还实现了另外一个接口CompletionStage,该接口是同步或者异步任务的某一个阶段。

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
    //无参构造函数
    public CompletableFuture() {
    }
}

我们通过一个例子熟悉下CompletableFuture的用法:

public static void main(String[] args) {
    CompletableFuture<String> completableFuture = new CompletableFuture<>();
    Executors.newCachedThreadPool().submit(()->{
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        completableFuture.complete("Hello JavaCodeGirl");
    });
    try {
        System.out.println("非阻塞获取结果为:" + completableFuture.getNow(null));
        System.out.println("阻塞获取结果为:" + completableFuture.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

}

CompletableFuture除了具备Future的特性之外,我们还可以利用它提供的静态方法supplyAsync和runAsync来执行异步任务。

supplyAsync类似Callable接口,可返回指定类型的结果。

runAsync的任务类型为Runnable,只关注任务本身的运行,不返回结果。

//supplyAsync执行任务
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()->100);
System.out.println(future.get());
//runAsync执行任务
CompletableFuture.runAsync(()->{
    System.out.println("Hello Java");
});

CompletableFuture允许将一个任务的执行结果继续传递到下一个任务,可以形成一个任务链。

thenApply/thenApplyAsync:以同步/异步的方式执行上一个异步任务的结果。

thenAccept/thenAcceptAsync:以同步/异步的方式消费上一个异步任务的结果。

thenRun/thenRunAsync:以同步/异步的方式执行Runnable任务。

掌握CompletableFuture的用法,还需要了解JDK1.8引入的Stream流和Lambda表达式,今天先对它有个初印象,下个系列勾勾就和大家一起学习Java8流。

 CompletableFuture.supplyAsync(()->{
     return "Hello Java";
 }).thenApply(e->{
     return e + "Code Girl";
 }).thenAccept(m ->{
     System.out.println(m.length());
 }).thenRun(()->{
     System.out.println("不想接收任务,只想做自己的事情");
 });

CompletableFuture还提供将多个Future合并为一个的thenCompose和thenCombine方法,大家可以自己了解一下喔。

结束语

并发编程系列的学习文章在今天就结束了,当然还有很多勾勾没有写出来的,比如原子类、ForkJoin、并发队列等,在后面的文章中如果涉及到我们再学习。勾勾写学习笔记也持续了一个月了,感谢关注陪伴勾勾的每一位小伙伴。

学完每个系列的知识,我们都应该检验下自己的掌握程度,上学的时候都是通过考试来校验,我们就通过面试题和实战来检验了。

接下来勾勾会推出并发编程面试题分析文章,如果你有面试经历,可以分享给勾勾!!!

勾勾的面试题目有些是勾勾作为面试官面试别人的题目,有的是勾勾作为面试者被别人虐的题目,也有的是勾勾后者脸皮从其他人那里要来的,总之都是100%的真题啦!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值