分析

  • Future模式的核心在于:去除了主函数的等待时间,并使得原本需要等待的时间段可以用于处理其他业务逻辑。

  • Future模式有点类似于商品订单。在网上购物时,提交订单后,在收货的这段时间里无需一直在家里等候,可以先干别的事情。类推到程序设计中时,当提交请求时,期望得到答复时,如果这个答复可能很慢。传统的是一直持续等待直到这个答复收到之后再去做别的事情,但如果利用Future模式,其调用方式改为异步,而原先等待返回的时间段,在主调用函数中,则可以用于处理其他事务。

总结来说:<mark>就是在调用一个很复杂的函数时,不需要等待其完成,而是采用异步的方式执行其他的代码</mark>

一个同步的例子

public class SyncInvoker {


    public static void main(String[] args) throws InterruptedException {
        //get方法的阻塞导致了主函数的阻塞
        String result = get();
        System.out.println(result);
    }

    private static String get() throws InterruptedException {

        //模拟一个很耗时的操作
        Thread.sleep(10_000);
        return "FinISH";
    }
}
  • 执行的get操作会阻塞住主线程的工作

Future模式

先设计一个接口,代表未来的凭据,相当于***

/** * 获取结果 * Furture 代表未来的凭据,相当于买的火车票 * @param <T> */
public interface Furture<T> {

    T get() throws InterruptedException;

}

实现Future接口
有两个方法

  • 一个方法交给其他线程在完成任务后,将结果放入到结果集
  • 一个方法负责提取结果集的数据,如果此时没用做完,就陷入阻塞
/** * 一个Future的实现类 * 有两个方法 * 一个方法交给其他线程在完成任务后,将结果放入到结果集 * 一个方法负责提取结果集的数据,如果此时没用做完,就陷入阻塞 * @param <T> */
public class AsynFuture<T> implements Future<T> {

    private volatile boolean done = false;

    private T result;

    /** * 当完成工作后,负责写入数据 * @param result */
    public void done(T result){
        synchronized (this){
            this.result = result;
            this.done = true;
            this.notifyAll();
        }
    }


    /** * 判断是否完成,如果完成,将结果返回 * 如果没有,陷入阻塞 * @return * @throws InterruptedException */
    @Override
    public T get() throws InterruptedException {
        synchronized (this){
            while (!done){
                this.wait();
            }
        }
        return result;
    }
}

设计一个桥梁桥接Future和FutureTask
主要思想是:

  • 设计一个线程执行FutureTask的任务,在执行完成后,放到asynFuture的结果集中
  • 返回asynFuture的结果集,供主方法自由的调用
/** * 桥接Future和FutureTask */
public class FutureService {

    public <T> Future<T> submit(FutureTask<T> task) {

        AsynFuture<T> asynFuture = new AsynFuture<>();

        //创建一个线程专门负责执行任务
        new Thread(()->{
            T result = task.call();
            asynFuture.done(result);
        }).start();

        return asynFuture;
    }

    public <T> void submit(FutureTask<T> task, Consumer<T> consumer) {

        AsynFuture<T> asynFuture = new AsynFuture<>();

        //创建一个线程专门负责执行任务
        new Thread(()->{
            T result = task.call();
            asynFuture.done(result);
            consumer.accept(result);
        }).start();
    }
}

测试

public class AsynClient {
    public static void main(String[] args) throws InterruptedException {
        FutureService futrueService = new FutureService();

        Future<String> future = futrueService.submit(() -> {
            try {
                Thread.sleep(5_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "FINISH";
        });
        System.out.println("我先去忙啦");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("我看看你完成没有");

        System.out.println(future.get());

// futrueService.submit(()->{
// try {
// Thread.sleep(3_000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// return "FINISH";
// },System.out::println);
// System.out.println("我先去忙啦");

    }
}

结果:

我先去忙啦
我看看你完成没有
FINISH

  • 可以看到代码并没有因为任务的执行而陷入阻塞