一种并行设计模式(Future模式 与 JDK内置实现)

Future模式是一种并行设计模式,原理是当你申请资源时,立即返回一个虚拟的资源(通常这个时候在后台异步去申请真正资源),当真正需要使用资源的时候,再将对虚拟资源的调用传递给成真正的资源(如果这个时候真正资源依然没有申请到,则阻塞)。

当一个操作耗时比较多时,可以将耗时操作拆分成多个独立的不太耗时的子操作,使子操作并行执行,各子操作异步保存执行结果以供主操作随时使用。

Future模式可用于服务端异步回调,将耗时的操作并行化,再通过回调方式将结果合并。

Future构造时生成了虚拟的结果,使用这个结果越晚就越不容易阻塞,所以,从生成虚拟资源到真正使用资源的间隔越大,Future模式的功效越大。


场景:一个业务流程有三步,1:从网络获取10个数据(2s);2:读取本地文件到内存(1s),检查是否包含获取到的数据,将没有包含的保存到文件;3:计算新文件的总大小(1s)。


分析可知:“本地文件到内存”与“网络获取10个数据”是两个耗时的操作,他们彼此是独立的(只有在检查是否包含的时候才有交集),因此可以并行操作,"计算新文件的总大小"也是一个耗时操作,然而它依赖第二步的完成,因此它必需和第二步同步执行,不能并行。


设计:


示例代码:

import java.util.concurrent.*;

interface Data {
    String getData();
}
class NetData implements Data {
    private String data;
    public NetData() {
        try {
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {
        }
        this.data = "真实数据";
    }
    @Override
    public String getData() {
        return data;
    }
}
class FutureData implements Data {
    private String realData;
    private  volatile boolean isReady;
    public FutureData() {
        // 异步准备数据
        new Thread(new Runnable() {
            @Override
            public void run() {
                String realData = new NetData().getData();// 耗时操作
                synchronized (FutureData.this) {
                    if (isReady) return;
                    isReady = true;
                    FutureData.this.realData = realData;
                    FutureData.this.notifyAll();// 不能直接notifyAll(),必需和synchronized (FutureData.this)保持一致
                }
            }
        }).start();
    }
    @Override
    public synchronized String getData() {
        // 使用while,防止唤醒后数据还未准备好
        while (!isReady) {// 使用while,防止唤醒后数据还未准备好
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        return realData;
    }
}

/**
 * Java标准库已经内置了 Future 模式的实现,通过FutureTask和Callable API实现
 * 其内部机制自动保证同步和阻塞,无需关心上文FutureData中的同步及wait/notify机制等问题,非常简洁
 * 而且具有异步任务取消等功能,如:
 */
class JDKFutureTask implements Data {
    private FutureTask<String> futureTask;
    public JDKFutureTask() {
        futureTask = new FutureTask<>(new Callable<String>() {// 需要的数据类型是String,使用泛型实现!
            @Override
            public String call() throws Exception {
                String realData = new NetData().getData();// 耗时操作
                return realData;
            }
        });
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(futureTask);
        executorService.shutdown();
    }
    @Override
    public String getData() {
        String data = null;
        try {
            data = futureTask.get();
        } catch (Exception e) {}
        return data;
    }
}
// Others ...
class FileLoader {
    String loadFile() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
        return "abc";
    }
}
class FileCounter {
    int countFile() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
        return 0;
    }
}
public class Test {
    private static void doTask(Data data) {// 这里 data,只是先保留一个资源引用,尚未真正使用资源,可以并行化
        String file = new FileLoader().loadFile();
        if (file.contains(data.getData())) {// 在这里,data.getData() 才是真正要使用资源
            // Nop
        }
        int countFile = new FileCounter().countFile();
    }
    // 自定义的Future方式和JDK FutureTask 方式耗时基本相同,而普通编程方式耗时最长。
    public static void main(String[] args) throws Exception {
        // 普通方式,原始阻塞方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                long s = System.currentTimeMillis();
                doTask(new NetData());
                System.out.println("使用普通方式耗时:" + (System.currentTimeMillis() - s));
            }
        }).start();

        // Future设计模式
        new Thread(new Runnable() {
            @Override
            public void run() {
                long s = System.currentTimeMillis();
                doTask(new FutureData());
                System.out.println("使用 Future设计模式耗时:" + (System.currentTimeMillis() - s));
            }
        }).start();

        // JDK FutureTask 方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                long s = System.currentTimeMillis();
                doTask(new JDKFutureTask());
                System.out.println("使用JDK FutureTask 方式耗时:" + (System.currentTimeMillis() - s));
            }
        }).start();
    }
}

打印结果:

使用 Future设计模式耗时:3003
使用JDK FutureTask 方式耗时:3006
使用普通方式耗时:4004


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值