前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >异步编程还得看JDK8

异步编程还得看JDK8

作者头像
Java极客技术
发布2022-12-04 10:35:39
1820
发布2022-12-04 10:35:39
举报
文章被收录于专栏:Java极客技术Java极客技术

话说,不看不知道,都说JDK7当时升级JDK8的时候,升级了非常多的内容,不得不说,这升级真的大。

什么是异步编程

在很多时候,我们在进程中使用单一线程从头到尾地执行程序,比如程序向另外一台服务器发出请求,由于网络等外部原因,此种通信任务往往会耗费大量时间,进程如果在此期间仅仅只能等待网络或网络上其他机器的响应,将严重地降低了性能。

如果程序调用某个方法,等待其执行全部处理后才能继续执行,我们称其为同步的。相反,在处理完成之前就返回调用方法则是异步的。

我们在编程语言的流程中添加了异步控制的部分,这部分的编程可以称之为异步编程。

JDK中的异步编程

Future

Future模式在 JDK5 的时候就有, Future模式,只是发起了耗时操作,函数立马就返回了,真正执行具体操作由另外一个工作线程去完成,并不会阻塞客户端线程。所以在工作线程执行耗时操作的时候客户端无需等待,可以继续做其他事情,等到需要的时候再向工作线程获取结果。

举个最简单的例子,我们烧水的时候么,不用一直在炉子旁边看着,在烧水的过程中,我们需要做一些其他的事情,比如去写一会代码,但是在你去写代码之前,会给你一个假的结果,比如,我已经烧开了,但是,在你去写代码的时候,他就开始疯狂加火,等到水烧开为止,等到你口渴想倒水的时候,发现水是已经烧开的,也就是说,当你在写代码之前的时候收到的是个假的结果。

实际上,Future 模式无法立即给出你想要的结果,但它会给你一个契约,之后你可以随时通过这个契约来获取你想要的结果。

异步模式主要是和同步模式进行对比的,我们画个图来看看。

黄色区域的位置的Y轴长度则表示的是你需要等待的所有时间,在这个时间内,你没有办法做任何的事情,只能在这里等着,但是异步的话,就完全不用这个样子了。

在JDK中Future模式有一套完整的实现。

我们来写案例代码实验一下:

没有是用 Future 的代码

NormalThreadTest

代码语言:javascript
复制
public class NormalThreadTest {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        // 开启购买厨具线程
        ShoppingThread shopping = new ShoppingThread();
        shopping.start();
        shopping.join(); // 保障厨具购买并送货
        // 获取到购买厨具
        KitchenWare kc = shopping.kc;

        // 买食材
        FoodMaterial fm = new FoodMaterial();
        Thread.sleep(2000);
        System.out.println("第二步: 食材已经到位");
        // 烹饪美食
        cooking(kc, fm);
        System.out.println("第三步: 美食烹饪完成");
        long end = System.currentTimeMillis();
        System.out.println("烹饪美食时间为:" +  (end - start));
    }


    /**
     * 定义网上购物厨具线程
     * @author Administrator
     *
     */
    static class ShoppingThread extends Thread {

        // 厨具对象引用
        private KitchenWare kc;

        @Override
        public void run() {
            System.out.println("第一步: 网上下单");
            System.out.println("第一步: 等待厨具");
            try {
                Thread.sleep(5000); // 等待厨具时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第一步: 快递送货");
            // 生产厨具
            kc = new KitchenWare();
        }
    }

    /**
     * 厨具类
     * @author Administrator
     *
     */
    static class KitchenWare {

    }

    /**
     * 食材类
     * @author Administrator
     *
     */
    static class FoodMaterial {

    }

    /**
     * 定义烹饪食物的方法
     * @param kc
     * @param fm
     */
    static void cooking(KitchenWare kc, FoodMaterial fm) {

    }
}

运行结果

代码语言:javascript
复制
第一步: 网上下单
第一步: 等待厨具
第一步: 快递送货
第二步: 食材已经到位
第三步: 美食烹饪完成
烹饪美食时间为:7043

已经使用Future的代码

FutureThreadTest

代码语言:javascript
复制
public class FutureThreadTest {


    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();

        Callable<KitchenWare> callable = new Callable<KitchenWare>() {
            public KitchenWare call() throws Exception {
                System.out.println("第一步: 网上下单");
                System.out.println("第一步: 等待厨具");
                try {
                    Thread.sleep(5000); // 等待厨具时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第一步: 快递送货");
                return new KitchenWare();
            }

        };
        // 包装为异步执行的对象
        FutureTask<KitchenWare> task = new FutureTask<>(callable);
        new Thread(task).start();

        // 买食材
        FoodMaterial fm = new FoodMaterial();
        Thread.sleep(2000);
        System.out.println("第二步: 食材已经到位");

        if (!task.isDone()) {
            System.out.println("厨具还没有到.....");
        }
        // 通过阻塞形式获取到异步块执行的结果
        KitchenWare kc = task.get(); // 阻塞
        // 烹饪美食
        cooking(kc, fm);
        System.out.println("第三步: 美食烹饪完成");
        long end = System.currentTimeMillis();
        System.out.println("烹饪美食时间为:" +  (end - start));
    }


    /**
     * 厨具类
     * @author Administrator
     *
     */
    static class KitchenWare {

    }

    /**
     * 食材类
     * @author Administrator
     *
     */
    static class FoodMaterial {

    }

    /**
     * 定义烹饪食物的方法
     * @param kc
     * @param fm
     */
    static void cooking(KitchenWare kc, FoodMaterial fm) {

    }
}

执行结果:

代码语言:javascript
复制
第一步: 网上下单
第一步: 等待厨具
第二步: 食材已经到位
厨具还没有到.....
第一步: 快递送货
第三步: 美食烹饪完成
烹饪美食时间为:5027

这个是JDK5中就有的 Future 来实现 异步编程的,那么接下来我们看1.8的异步编程。

CompletableFuture

Future 虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成,我们通过上面的代码也完全能看出来。

为什么在JDK5之后,又推出新的异步编程,因为使用 Future 要么使用阻塞,在 future.get() 的地方等待 Future 返回的结果,这时又变成同步操作。要么使用 isDone() 轮询地判断 Future 是否完成,这样会耗费CPU的资源。所以阿粉猜测所以在JDK8又推出了 CompletableFuture。

之前 Future 需要等待 isDone 为 true 才能知道任务跑完了。或者就是用 get 方法调用的时候会出现阻塞。而使用 CompletableFuture 的使用就可以用 then , when 等等操作来防止以上的阻塞和轮询 isDone 的现象出现。

CompletableFuture 有四个方法来创建CompletableFuture对象

代码语言:javascript
复制
public static CompletableFuture<Void>   runAsync(Runnable runnable)

public static CompletableFuture<Void>   runAsync(Runnable runnable, Executor executor)

public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier)

public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier, Executor executor)

Asynsc表示异步,而supplyAsync与runAsync不同在与前者异步返回一个结果,后者是void.第二个函数第二个参数表示是用我们自己创建的线程池,否则采用默认的ForkJoinPool.commonPool()作为它的线程池.其中Supplier是一个函数式接口,代表是一个生成者的意思,传入0个参数,返回一个结果.

我们写一个最简单的测试代码

代码语言:javascript
复制
public static void test2() throws Exception {
        //supplyAsync内部使用ForkJoinPool线程池执行任务
        CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
            //模拟执行耗时任务
            System.out.println("task doing...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //返回结果
            return "100";
        }).whenComplete((v,r)->{
            System.out.println("计算结果是: "+v);
        });
        //CompletableFuture里使用的线程池里的线程默认是daemon的。main线程结束后,整个程序也
        //结束了,这里将main线程join后任务里的代码才可以执行完
         Thread.currentThread().join();
    }

而使用 CompletableFuture 能有效的避开使用 Futrue 出现的缺点。

看来,JDK 每一次的更新换代,不光是加了一些新的内容,而且像开发一样,每次迭代的时候,同时也会更新之前的一些不完美的内容,不是么?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java极客技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是异步编程
  • JDK中的异步编程
  • CompletableFuture
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档