我是靠谱客的博主 文艺板凳,最近开发中收集的这篇文章主要介绍Java 8系列之Future一、基本概念二、异步调用三、几种异步调用的时间对比(比较粗糙)四、几种异步调用的方式对比,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Java 8系列:
Java 8系列之Lambda表达示
Java 8系列之StreamApi
Java 8系列之Collector
Java 8系列之Optional
Java 8系列之Future

一、基本概念

1.并发与并行

2.同步API与异步API

同步API:你调用了某个方法,调用方在被调用方运行的过程中会等待,被调用方运行结束返回,调用方取得被调用方的返回值并继续运行。即使调用方和被调用方在不同的线程中运行,调用方还是需要等待被调用方结束运行,这就是阻塞式调用。
异步API你调用了某个方法,被调用方直接返回,或者至少在被调用方计算完成之前,将它剩余的计算任务交给另一个线程去做,该线程和调用方是异步的,这就是非阻塞式调用。

二、异步调用

1.java8之前实现异步调用的方式

首先写一个简单的例子来了解异步调用:使用Future以异步的方式执行一个耗时的操作,这种编程方式让我们的线程可以在线程池中以并发方式调用另一个线程执行耗时操作的同时,去执行一些其他的任务。

    //创建线程池
    ThreadPoolExecutor executor  = new ThreadPoolExecutor(6,6,2, TimeUnit.MINUTES,new LinkedBlockingDeque<Runnable>());

    //任务(耗时就用sleep)
    public Double task(Long start,String name)  {
        double taskResult = Math.random() * 1000;
        try {
            Thread.sleep((long)taskResult);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行"+name+",耗时:" + (System.nanoTime() - start) / 1_000_000 + " msecs");
        return taskResult;
    }

    @Test
    public void testFuture() throws Exception{
        long start = System.nanoTime();
        //1.向线程池提交异步任务1,使用future来接收线程结果
        Future<Double> future = executor.submit(()->task(start,"任务1"));
        //2.主流程执行任务2
        Double task2Result = task(start,"任务2");
        //3.future.get(),阻塞操作,直到获取异步操作的结果
        Double task1Result = future.get();

        System.out.println("全部计算完成,耗时:"+ (System.nanoTime() - start) / 1_000_000 + " msecs");
        System.out.println("task1Result:"+ task1Result);
        System.out.println("task2Result:"+ task2Result);
    }

结果:

执行任务2,耗时:432 msecs
执行任务1,耗时:849 msecs
全部计算完成,耗时:849 msecs
task1Result:815.5350145603924
task2Result:398.9337349758296

2.使用java8的CompletableFuture来实现异步调用

    @Test
    public void testCompletableFuture() throws InterruptedException, ExecutionException {
        long start = System.nanoTime();
        //1.向线程池提交异步任务1,使用CompletableFuture来接收线程结果
        CompletableFuture<Double> task2 = CompletableFuture.supplyAsync(() -> task(start, "任务1"),executor);
        //2.主流程执行任务2
        Double task1Result = task(start, "任务2");
        //3.future.get(),阻塞操作,直到获取异步操作的结果
        Double task2Result = task2.get();
        System.out.println("全部计算完成,耗时:"+ (System.nanoTime() - start) / 1_000_000 + " msecs");
        System.out.println("task1Result:"+ task1Result);
        System.out.println("task2Result:"+task2Result );
    }

结果:

执行任务2,耗时:597 msecs
执行任务1,耗时:1024 msecs
全部计算完成,耗时:1024 msecs
task1Result:564.4290801402495
task2Result:992.1778479980603

其中CompletableFuture类提供了大量的工厂方法,使用这些方法能更容易地完成整个流程,还不用担心实现的细节。

supplyAsync方法

接受一个生产者(Supplier)作为参数,返回一个CompletableFuture对象,该对象完成异步执行后会读取调用生产者方法的返回值。

如不指定线程池,生产者方法会交由ForkJoinPool池中的某个执行线程(Executor)运行

    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
        return asyncSupplyStage(asyncPool, supplier);
    }

也可使用supplyAsync方法的重载版本,第二个参数传递线程池

三、几种异步调用的时间对比(比较粗糙)


    List<String> taskList = Arrays.asList("任务1",
            "任务2",
            "任务3",
            "任务4",
            "任务5",
            "任务6");

    //任务
    public Long taskUpgrade(Long start,String name)  {
        long sum=0;
        for (long i = 0; i <1000000000L ; i++) {
            sum=sum+i;
        }
        //System.out.println("执行"+name+",耗时:" + (System.nanoTime() - start) / 1_000_000 + " msecs");
        return sum;
    }
    
    /**
     * 使用流顺序计算
     * @param start
     * @return
     */
    public List<Long> getTaskResult(long start) {
        return taskList.stream()
                .map(taskName -> taskUpgrade(start,taskName))
                .collect(toList());
    }

    /**
     * 使用流并行计算
     * @param start
     * @return
     */
    public List<Long> getTaskResultParallel(long start) {
        return taskList.parallelStream()
                .map(taskName -> taskUpgrade(start,taskName))
                .collect(toList());
    }

    /**
     * 异步运算:使用默认执行器
     * @param start
     * @return
     */
    public List<Long> getTaskResultFuture(long start) {
        List<CompletableFuture<Long>> futureList = taskList.stream()
                .map(taskName -> CompletableFuture.supplyAsync(() -> taskUpgrade(start,taskName)))
                .collect(toList());
        //CompletableFuture 类中的 join 方法和 Future 接口中的 get 有相同的含义
        return futureList.stream()
                .map(CompletableFuture::join)
                .collect(toList());
    }

    /**
     * 异步运算:使用定制的执行器
     * @param start
     * @return
     */
    public List<Long> getTaskResultFuture1(long start) {
        List<CompletableFuture<Long>> futureList = taskList.stream()
                .map(taskName -> CompletableFuture.supplyAsync(() -> taskUpgrade(start,taskName), executor))
                .collect(toList());
        //CompletableFuture 类中的 join 方法和 Future 接口中的 get 有相同的含义
        return futureList.stream()
                .map(CompletableFuture::join)
                .collect(toList());
    }

    @Test
    public void test3() {
        long start = System.nanoTime();
        getTaskResult(start);
        System.out.println("使用流顺序计算:" + (System.nanoTime() - start) / 1_000_000 + " msecs");
        start = System.nanoTime();
        getTaskResultParallel(start);
        System.out.println("使用流【并行】计算:" + (System.nanoTime() - start) / 1_000_000 + " msecs");
        start = System.nanoTime();
        getTaskResultFuture(start);
        System.out.println("异步运算:使用【默认执行器】,【并发】:" + (System.nanoTime() - start) / 1_000_000 + " msecs");
        start = System.nanoTime();
        getTaskResultFuture1(start);
        System.out.println("异步运算:使用【定制的执行器】,【并发】:" + (System.nanoTime() - start) / 1_000_000 + " msecs");

    }

结果:

使用流顺序计算:1607 msecs
使用流【并行】计算:280 msecs
异步运算:使用【默认执行器】,【并发】:521 msecs
异步运算:使用【定制的执行器】,【并发】:284 msecs

并行并发不相伯仲,究其原因都一样:它们内部采用的是同样的通用线程池,默认都使用固定数目的线程,具体线程数取决于Runtime.getRuntime().availableProcessors() 的返回值。
CompletableFuture 具有一定的优势,因为它允许你对执行器( Executor )进行配置,尤其是线程池的大小

两种并发情况,结果不同,是因为默认执行器是使用的ForkJoinPool做为线程池,默认线程数量等于运行计算机上的处理器数量,小于我们自定义的线程池的线程数量6

四、几种异步调用的方式对比

模拟执行任务,并把任务结果放到list中

    //创建线程池
    ThreadPoolExecutor executor  = new ThreadPoolExecutor(20,20,2, TimeUnit.MINUTES,new LinkedBlockingDeque<Runnable>());


    List<String> taskList = Arrays.asList("任务1",
            "任务2",
            "任务3",
            "任务4",
            "任务5",
            "任务6");

    //任务
    public Long task(Long start,String name)  {
        //Math.random() *
        long sum=0;
        for (long i = 0; i <1000000000L ; i++) {
            sum=sum+i;
        }
        //System.out.println("执行"+name+",耗时:" + (System.nanoTime() - start) / 1_000_000 + " msecs");
        return sum;
    }
    //任务,主要是为了执行countDownLatch.countDown();
    public Long task(Long start,String name,CountDownLatch countDownLatch)  {
        //Math.random() *
        long sum=0;
        for (long i = 0; i <1000000000L ; i++) {
            sum=sum+i;
        }
        countDownLatch.countDown();
        //System.out.println("执行"+name+",耗时:" + (System.nanoTime() - start) / 1_000_000 + " msecs");
        return sum;
    }

    /**
     * 异常调用:使用Callable,返回值用Future接收,使用get()方法阻塞
     * @throws Exception
     */
    public void useCallable() throws Exception{
        long start = System.nanoTime();
        List<Long> list=new ArrayList<>();
        List<Future<Long>> futureList = taskList
                .stream()
                .map(o -> executor.submit(() -> task(start, o)))
                .collect(Collectors.toList());
        for (Future<Long> f: futureList) {
            list.add(f.get());
        }
        System.out.println("使用Future,耗时:"+ (System.nanoTime() - start) / 1_000_000 + " msecs");
        //System.out.println("list:"+ list);
    }




    /**
     * 异常调用,使用CountDownLatch,await()方法来阻塞
     * @throws Exception
     */
    public void useCountDownLatch() throws Exception{
        long start = System.nanoTime();
        List<Long> list=new ArrayList<>();
        CountDownLatch countDownLatch = new CountDownLatch(6);
        List<Future<Long>> futureList = taskList
                .stream()
                .map(o ->executor.submit(()->task(start,o,countDownLatch)))
                .collect(Collectors.toList());
        countDownLatch.await();
        for (Future<Long> f: futureList) {
            list.add(f.get());
        }
        System.out.println("使用CountDownLatch,耗时:"+ (System.nanoTime() - start) / 1_000_000 + " msecs");
        //System.out.println("list:"+ list);
    }



    /**
     * 异常调用,使用CompletableFuture,join()方法来阻塞
     * @throws Exception
     */
    public void useCompletableFuture() throws Exception{
        long start = System.nanoTime();
        List<Long> list=new ArrayList<>();
        List<CompletableFuture<Long>> completableFutureList = taskList.stream()
                .map(o -> CompletableFuture.supplyAsync(() -> task(start, o), executor))
                .collect(Collectors.toList());

        CompletableFuture<Void> allOf = CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[0]));
        allOf.join();
        for (CompletableFuture<Long> c: completableFutureList) {
            list.add(c.get());
        }
        System.out.println("使用CompletableFuture,耗时:"+ (System.nanoTime() - start) / 1_000_000 + " msecs");
        //System.out.println("list:"+ list);
    }

    @Test
    public void test() throws Exception {
        useCallable();
        useCountDownLatch();
        useCompletableFuture();
    }

结果:

使用Future,耗时:433 msecs
使用CountDownLatch,耗时:417 msecs
使用CompletableFuture,耗时:287 msecs

结果不重要,只要展示下,多任务同时处理的几种机制,代码实现上来说复杂度差不多吧,方便以后项目中使用

最后

以上就是文艺板凳为你收集整理的Java 8系列之Future一、基本概念二、异步调用三、几种异步调用的时间对比(比较粗糙)四、几种异步调用的方式对比的全部内容,希望文章能够帮你解决Java 8系列之Future一、基本概念二、异步调用三、几种异步调用的时间对比(比较粗糙)四、几种异步调用的方式对比所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部