Java中Future模式的使用

前言

今天是1024,祝各位老板节日快乐!


傍晚打算下楼买点吃的,心中打定了注意,今晚要买【鸡蛋仔】和【手抓饼】,好家伙,这不素材就有了嘛,嘿嘿嘿!

于是我便迈着“六亲不认”的步伐出发了…

能不能再快点

❓路上,我思考了一个问题。那就是我到底需要耗费多长时间,才能握住简单的快乐?

按照流程是这样子的

  1. 骑着我心爱的小摩托出发
  2. 到达买鸡蛋仔的摊位,点一份鸡蛋仔,然后等老板做完
  3. 再到手抓饼摊位,点一份手抓饼
  4. 拿到手抓饼,骑车回家

假设路程来回是6分钟,制作鸡蛋仔的时间是10分钟,制作手抓饼的时间的5分钟

那么我总共就需要【21】分钟,才可以坐在电脑旁,看剧干饭

如果想要耗时短一点,那么上面这个流程就不是我们需要的,而应该是这样子的

  1. 骑着我心爱的小摩托出发
  2. 到达买鸡蛋仔的摊位,点一份鸡蛋仔,跟老板说,我待会来拿
  3. 再到手抓饼摊位,点一份手抓饼
  4. 拿到手抓饼之后,再返回鸡蛋仔摊位,等待取鸡蛋仔
  5. 骑车回家

那么此时,我们耗费的时间就只有【16】分钟,因为这次我在点了鸡蛋仔之后,老板在烹饪的期间,先去完成了一样耗时在10分钟之内的操作,就是买我要的手抓饼

当然,我肯定是用第二种方式的。。。

落地一下

梳理一下,刚才的流程

  • 第一种流程就是典型,按照顺序执行
  • 而第二种流程就是使用到了异步的思想

那么不妨来敲下代码,将流程给展现出来

首先我们得有一个买吃的对象

/**
 * @Author: Amg
 * @Date: Created in 20:22 2021/10/24
 * @Description: 构造“买点吃的”对象
 */
public class BuySomethingToEat {

    /**
     * 食物名称
     */
    private String foodName;
    /**
     * 制作时长单位
     */
    private TimeUnit unit;
    /**
     * 制作时长
     */
    private Long waitSecond;

    public BuySomethingToEat(String foodName, TimeUnit unit, Long waitSecond) {
        this.foodName = foodName;
        this.unit = unit;
        this.waitSecond = waitSecond;
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", BuySomethingToEat.class.getSimpleName() + "[", "]")
                .add("foodName='" + foodName + "'")
                .add("unit=" + unit)
                .add("waitSecond=" + waitSecond)
                .toString();
    }
     /**
     * 处理食材
     * @return
     */
    public String handlerFood() {
        System.out.println(String.format("正在准备食物:[%s],单位:[%s],等待时长:[%s] ",foodName,waitSecond,unit.toString()));
        try {
            unit.sleep(waitSecond);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return this.toString() + " is done!";
    }
}

按照顺序的思想执行

在这里插入图片描述

/**
 * @Author: Amg
 * @Date: Created in 21:09 2021/10/24
 * @Description: 常规做法
 */
public class Convention {

    public static void main(String[] args) throws Exception {

        final long start = System.currentTimeMillis();
        //我们不可能有时间真的等待上10分钟,所以用秒来替代
        final BuySomethingToEat eatOne = new BuySomethingToEat("鸡蛋仔", TimeUnit.SECONDS, 10L);
        final BuySomethingToEat eatTwo = new BuySomethingToEat("手抓饼", TimeUnit.SECONDS, 5L);

        System.out.println(eatOne.handlerFood());
        System.out.println(eatTwo.handlerFood());

        System.out.println("买齐了,回家!");
        //最后加上来回的6分钟
        System.out.println("总共耗时:" + ((System.currentTimeMillis() - start) / 1000 + 6));

    }
}
//output 
正在准备食物:[鸡蛋仔],单位:[10],等待时长:[SECONDS] 
BuySomethingToEat[foodName='鸡蛋仔', unit=SECONDS, waitSecond=10] is done!
正在准备食物:[手抓饼],单位:[5],等待时长:[SECONDS] 
BuySomethingToEat[foodName='手抓饼', unit=SECONDS, waitSecond=5] is done!
买齐了,回家!
总共耗时:15

按照异步的思想执行

异步的思想

在引入今天的主角之前,先来理解一下异步的思想,还是先来一个图

在这里插入图片描述

异步的思想就是:准备好了,再告诉我,我先去干其他的事情

对应图上就是,在鸡蛋仔制作中,由于耗时比较久,我就不在这里干等了,先去点手抓饼(完成其他耗时没有那么久的操作)

今天的猪脚,jdk中的Future模式

由于Future模式(核心思想就是异步调用)非常的常用,因此JDK内部已经为我们准备好了实现

简单说,通过Future,你可以定义一个任务,让这个任务执行,在未来某个时间,可以通过API获取到数据,如果获取不到,就会一直阻塞

//Future接口里面一共提供了5个方法,想了解更多?那就直接去阅读这个接口的文档就好了
public interface Future<V> {

   
    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

这是个接口,而我们真正使用到的是它的子类FutureTask

我们观察一下,FutureTask这个类

在这里插入图片描述

可以发现,他的构造方法需要传入一个实现了Callable的类或者实现了Runnable的类,既然如此,我们就再新建一个BuyTask好了

/**
 * @Author: Amg
 * @Date: Created in 21:15 2021/10/24
 * @Description: TODO
 */
public class BuyTask implements Callable<String> {

    private BuySomethingToEat buySomethingToEat;

    public BuyTask(BuySomethingToEat buySomethingToEat) {
        this.buySomethingToEat = buySomethingToEat;
    }

    @Override
    public String call() throws Exception {
        return buySomethingToEat.handlerFood();
    }
}

然后,使用FutureTask这个类创建两个任务交由线程池去执行

public class MyFutureTask {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        final BuySomethingToEat eatOne = new BuySomethingToEat("手抓饼", TimeUnit.SECONDS, 10L);
        final BuySomethingToEat eatTwo = new BuySomethingToEat("鸡蛋仔", TimeUnit.SECONDS, 5L);
        final long start = System.currentTimeMillis();

        ExecutorService executorService = Executors.newFixedThreadPool(2);

        final FutureTask<String> task1 = new FutureTask<>(new BuyTask(eatOne));
        final FutureTask<String> task2 = new FutureTask<>(new BuyTask(eatTwo));

        executorService.submit(task1);
        executorService.submit(task2);

        System.out.println(task2.get());
        System.out.println(task1.get());
        System.out.println("买齐了");
        System.out.println("总共耗时:" + (System.currentTimeMillis() - start) / 1000);

        if (!executorService.isShutdown()) executorService.shutdown();
    }
}

//output
正在准备食物:[鸡蛋仔],单位:[5],等待时长:[SECONDS] 
正在准备食物:[手抓饼],单位:[10],等待时长:[SECONDS] 
BuySomethingToEat[foodName='鸡蛋仔', unit=SECONDS, waitSecond=5] is done!
BuySomethingToEat[foodName='手抓饼', unit=SECONDS, waitSecond=10] is done!
买齐了
总共耗时:16

//或者是
正在准备食物:[手抓饼],单位:[10],等待时长:[SECONDS]
正在准备食物:[鸡蛋仔],单位:[5],等待时长:[SECONDS]  
BuySomethingToEat[foodName='鸡蛋仔', unit=SECONDS, waitSecond=5] is done!
BuySomethingToEat[foodName='手抓饼', unit=SECONDS, waitSecond=10] is done!
买齐了
总共耗时:16

总结

Future模式用途非常的广泛

  • 常见的有SQL异步调用,当某个SQL耗时稍微久一点,可以先执行与SQL流程不相关的其他步骤先,节省时间;
  • 还有计算计算密集的操作
  • 还有异步调用其他系统接口

Future还有更加高级的用法,当前只是简单介绍了一下Future以及他的实现类FutureTask是怎么使用起来的,对于理解Future思想和简单使用也是足够得了

但是关于Future还会有更加高级的用法,例如guava中对Future的增强,后续也会进一步深入的介绍

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值