Future和CompletableFuture的用法和区别

timo 1年前 ⋅ 851 阅读

Future的用法

多线程场景时,一般是实现runnable接口,覆写run方法,返回值是void类型,因此这种情况下不需要线程的返回结果。
如果需要线程的返回结果,就需要用callable接口来代替了。
callable用法和runnable一样,只不过覆写的是call方法,该方法有一个泛型返回值类型,可以根据需要指定。
那么何时到Future呢?当你启动callable线程时,就可以声明一个Future对象,用于接收返回结果。
Futrue可以监视目标线程调用call的情况,当你调用Future的get()方法以获得结果时,调用方的线程就被阻塞,直到目标线程的call方法结束并返回结果。
Future接口,一般都是取回Callable执行的状态用的。其中的主要方法:

  cancel,取消Callable的执行,当Callable还没有完成时
  get,获得Callable的返回值
  isCanceled,判断是否取消了
  isDone,判断是否完成

举个栗子。
四个刚需(线程)去买房摇号,future获取摇号结果。摇号结果未出,就一直阻塞。

public class FutureTest {
 
    /**
     * 买房摇号
     */
    public static class Yaohao implements Callable<Integer> {
        /**
         * 返回摇号结果
         * @return 0:中签   1:没中
         * @throws Exception
         */
        @Override
        public Integer call() throws Exception {
            Random random = new Random();
            //模拟摇号,10天内出结果
            TimeUnit.SECONDS.sleep(random.nextInt(10));
            int result = random.nextInt(2);
            System.out.println("     "+Thread.currentThread().getName()+" is done!");
            return result;
        }
    }
 
    public static void main(String[] args) throws InterruptedException, ExecutionException {
 
        Yaohao gangxu1 = new Yaohao();
        Yaohao gangxu2 = new Yaohao();
        Yaohao gangxu3 = new Yaohao();
        Yaohao gangxu4 = new Yaohao();
        ExecutorService es = Executors.newCachedThreadPool();
        Future<Integer> result1 = es.submit(gangxu1);
        Future<Integer> result2 = es.submit(gangxu2);
        Future<Integer> result3 = es.submit(gangxu3);
        Future<Integer> result4 = es.submit(gangxu4);
        es.shutdown();
 
        System.out.println("刚需1,摇号结果:"+(result1.get()==1?"中签":"没中"));
        System.out.println("刚需2,摇号结果:"+(result2.get()==1?"中签":"没中"));
        System.out.println("刚需3,摇号结果:"+(result3.get()==1?"中签":"没中"));
        System.out.println("刚需4,摇号结果:"+(result4.get()==1?"中签":"没中"));
    }
 
}

CompletableFuture的用法

1.创建异步操作,runAsync(不支持返回值) 和 supplyAsync方法(支持返回值)
2.计算结果完成时的回调方法
    whenComplete:执行完当前任务的线程,继续执行 whenComplete 的任务。
    whenCompleteAsync: 执行完当前任务的线程,把whenCompleteAsync 的任务继续提交给线程池来执行。
    exceptionally:当前任务出现异常时,执行exceptionally中的回调方法。
3.thenApply 方法,当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化。
4.handle 方法
    handle 是执行任务完成时对结果的处理。
    handle 方法和 thenApply 方法处理方式基本一样。不同的是 handle 是在任务完成后再执行,还可以处理异常的任务。thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。
5.thenAccept 消费处理结果,接收任务的处理结果,并消费处理,无返回结果。
6.thenRun 方法,跟 thenAccept 方法不一样的是,不关心任务的处理结果。只要上面的任务执行完成,就开始执行 thenAccept 。
7.thenCombine 合并任务,thenCombine 会把 两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。
8.thenCompose 方法,thenCompose 方法允许你对两个 CompletionStage 进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。

实际的例子可以参考下面这篇文章,写得很简单明了。

 

Future和CompletableFuture的区别

Future在Java5就引入了。

优点:一定程度上让一个线程池内的任务异步执行了
缺点:传统回调最大的问题就是不能将控制流分离到不同的事件处理器中。例如主线程等待各个异步执行的线程返回的结果来做下一步操作,则必须阻塞在future.get()的地方等待结果返回。这时候又变成同步了。

CompletableFuture在Java8引入。

实现了Future和CompletionStage接口,保留了Future的优点,并且弥补了其不足。即异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。
可见,这种方式才是我们需要的异步处理。一个控制流的多个异步事件处理能无缝的连接在一起。

 

 

 

 

版权 本文为TIMO社区原创文章,转载无需和我联系,但请注明来自TIMO社区 http://timo.aikanmv.cn