多线程(10) — Future模式

  Future模式是多线程开发中常用常见的一种设计模式,它的核心思想是异步调用。在调用一个函数方法时候,如果函数执行很慢,我们就要进行等待,但这时我们可能不着急要结果,因此我们可以让被调者立即返回,让它在后台慢慢处理这个请求,对于调用者来说可以先处理一些其他事物,在真正需要数据的场合再去尝试获得需要的数据。对于Future模式来说,虽然它无法立即给出你需要的数据,但是它们返回一个契约给你,将来你可以凭借这个契约去重新获取你需要的信息。主要的角色有:

  • Main:系统启动,调用Client发出请求。
  • Client:返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData
  • Data:返回数据的接口。
  • FutureData:Future数据构造很快,但是是一个虚拟的数据,需要装配RealData
  • RealData:真实数据,其构造是比较慢的

  Future模式简单实现:一个核心接口Data,就是客户端希望获取的数据,在Future模式中,这个Data接口有两个重要的实现,一个是RealData,是真实数据,就是最终要获得的信息。另外一个是FutureData,它是用来提取RealData的一个订单,因此FutureData可以立即返回。

public interface Data {
    public String getResult();
}

  FutureData实现了一个快速返回的RealData包装,只是一个包装,或者说是虚拟实现,它可以很快构造并返回。当使用FutureData的getResult()方法,如果实际数据没准备好程序会阻塞,等RealData准备好并注入FutureData才最终返回数据。

注意:FutureData是Future模式的关键,实际上是真实数据RealData的代理,封装了获取RealData的等待过程。

public class FutureData implements Data {

    protected RealData realdata = null;
    protected boolean isReady = false;

    public synchronized void setRealData(RealData realdata){
        if(isReady){
            return;
        }
        this.realdata = realdata;
        isReady = true;
        notifyAll();
    }

    @Override
    public synchronized String getResult() {
        while(!isReady){
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        return realdata.result;
    }
}

  RealData是最终需要使用的数据模型,它的构造很慢,用sleep()函数模拟这个过程,简单地模拟一个字符串的构造。

public class RealData implements Data {

    protected final String result;
    public RealData(String para){
        // RealData的构造可能很慢,需要用户等待很久,这里用sleep模拟
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 10; i++) {
            sb.append(para);
            try {
                // 这里使用sleep代替一个很慢的操作过程
                Thread.sleep(100);
            } catch (InterruptedException e) {}
        }
        result = sb.toString();
    }
    @Override
    public String getResult() {
        return result;
    }
}

  接下来就是客户端程序,Client主要实现了获取FutureData,并开启构造RealData的线程,并在接受请求后,很快返回FutureData。注意,它不会等待数据真的构造完毕再返回,而是立即返回FutureData,即使这个时候FutureData内没有真实数据。

public class Client {
    public Data request(final String queryStr){
        final FutureData future = new FutureData();
        new Thread(){

            @Override
            public void run() {
                RealData realdata = new RealData(queryStr);
                future.setRealData(realdata);
            }

        }.start();
        return future;
    }
}
public static void main(String[] args) {
    Client client = new Client();
    // 这里立即返回,得到的是Future而不是RealData
    Data data = client.request("Hello");
    System.out.println("请求完成");
    try {
        Thread.sleep(2000);// 这个过程代替RealData被创建的过程,也就是自己的业务处理过程
    } catch (InterruptedException e) {}
    // 使用真实的数据
    System.out.println("数据 = "+data.getResult());
}

JDK中的Future模式

  JDK中,Future接口类似于模式中的契约,通过它,可以得到真实的数据。RunnableFuture继承了Future和Runnable俩接口,其中run()方法用于构造真实的数据,它有一个具体的实现FutureTask类,这个实现类有个内部类Sync,一些实质性的工作会委托Sync实现,而Sync类最终会调用Callable接口,完成实际数据的组装工作。Callable接口只有一个方法call(),它会返回需要构造的实际数据。这个Callable接口也是Future框架和应用程序之间的重要接口。要实现自己的业务系统,通常需要实现自己的Callable对象,此外,FutureTask类也是与应用密切相关,通常可以使用Callable实例构造一个FutureTask实例,并将它交给线程池。下面来举个使用的例子:

public class RealData implements Callable<String>{
    private String para;
    public RealDatajdk(String para){
        this.para = para;
    }

    @Override
    public String call() throws Exception {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 10; i++) {
            sb.append(para);
            Thread.sleep(100);
        }
        return sb.toString();
    }
}

  上述代码实现了Callable接口,它的call()方法会构造我们需要的真实数据并返回,当然这个过程可能是缓慢的,这里使用Thread.sleep()方法模拟它。

public class FutureMain {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTask<String> future = new FutureTask<String>(new RealData("a"));// 构造FutureTask
        ExecutorService executor = Executors.newFixedThreadPool(1);
        // 执行FutureTask,相当于上例中client.request("a")发送请求
        // 在这里开启线程进行RealData的call()方法执行
        executor.submit(future);
        System.out.println("请求完毕");
        try {
            // 这里可以做额外的数据操作,使用sleep代替其他业务逻辑处理
            Thread.sleep(2000);
        } catch (InterruptedException e) {}
        // 取得call()方法的返回值,如果call()方法还没有执行完,就会等待
        System.out.println("数据 = "+future.get());
    }
}

除基本的功能外JDK还为Future接口提供了一些简单的控制方法

boolean cancel(boolean mayInterruptIfRunning);           // 取消任务
boolean isCancelled();                                   // 是否已经取消任务
boolean isDone();                                        // 是否已完成
V get() throws InterruptedException, ExecutionException; // 取得返回对象
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;// 取得返回对象,可以设置超时时间

Guava对Future模式的支持

  JDK自带的简单Future模式,可以使用get()方法得到处理结果,但是这个方法是阻塞的,因此不利于高并发应用。在Guava中增强了Future模式,增加了对模式完成时的回调接口,使得Future完成时可以自动通知应用程序进行后续处理。使用Guava改写上一节中的FutureMain可以得到更好的效果。

public class FutureDemo {
    public static void main(String[] args) throws InterruptedException{
        ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
        ListenableFuture<String> task = service.submit(new RealData("x"));
        task.addListener(new Runnable(){
            @Override
            public void run() {
                System.out.print("异步处理成功:");
                try {
                    System.out.println(task.get());
                } catch (Exception e) {}
            }
        },MoreExecutors.directExecutor());
        System.out.println("main task done......");
        Thread.sleep(3000);
    }
}

  MoreExecutors.listeningDecorator()方法将一个普通的线程池包装为一个包含通知功能的Future线程池。第5行将Callable任务提交到线程池中,并得到一个ListenableFuture。与Future相比,ListenableFuture拥有完成时的通知功能,addListener向ListenableFuture中添加回调函数,即当Future执行完成后,执行addListener中第一个参数中代码

原文地址:https://www.cnblogs.com/wangyongwen/p/11335334.html

时间: 2024-09-29 04:28:59

多线程(10) — Future模式的相关文章

利用多线程实现Future模式

一.Futrue模式 客户端发送一个长时间的请求,服务端不需等待该数据处理完成便立即返回一个伪造的代理数据(相当于商品订单,不是商品本身),用户也无需等待,先去执行其他的若干操作后,再去调用服务器已经完成组装的真实数据. 该模型充分利用了等待的时间片段.简单来说就是,如果线程A要等待线程B的结果,那么线程A没必要等待B,直到B有结果,可以先拿到一个未来的Future,等B有结果是再取真实的结果. 在多线程中经常举的一个例子就是:网络图片的下载,刚开始是通过模糊的图片来代替最后的图片,等下载图片的

多线程设计模式 - Future模式之JAVA实现

在之前一篇博客中介绍了Future设计模式的设计思想以及具体实现,今天我们来讲一下使用JDK原生的包如何实现. JDK内置的Future主要使用到了Callable接口和FutureTask类. Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务.Callable接口的定义如下: public interface Callable<V> { /** * Computes a result, or throws an

多线程设计模式 - Future模式

Future模式是多线程开发中非常常见的一种设计模式,它的核心思想是异步调用.这类似我们日常生活中的在线购物流程,带在购物网看着一件商品时可以提交表单,当订单完成后就可以在家里等待商品送货上门.或者说更形象的是我们发送Ajax请求的时候,页面是异步的进行后台处理,用户无需等待请求的结果,可以继续浏览或操作其他内容. 如上图所示,客户端调用购物请求,服务端程序不等数据处理完成便立即返回客户端一个伪造的数据,(相当于订单,而不是真实的商品)这时候由服务端自己偷偷摸摸的发送了一个other call(

转多线程设计模式 - Future模式之JAVA原生实现

在之前一篇博客中介绍了Future设计模式的设计思想以及具体实现,今天我们来讲一下使用JDK原生的包如何实现. JDK内置的Future主要使用到了Callable接口和FutureTask类. Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务.Callable接口的定义如下: public interface Callable<V> { /** * Computes a result, or throws an

Java多线程编程中Future模式的详解&lt;转&gt;

Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Future模式,关于其他多线程设计模式的地址如下:关于其他多线程设计模式的地址如下:关于Master-Worker模式的详解: Java多线程编程中Master-Worker模式的详解关于Guarded Suspeionsion模式的详解: Java多线程编程中Guarded Suspeionsion模式

Java多线程Future模式

Java多线程Future模式有些类似于Ajax的异步请求Future模式的核心在于:去除了主函数的等待时间,并使得原本需要等待的时间段可以用于处理其他业务逻辑 假设服务器的处理某个业务,该业务可以分成AB两个过程,并且AB两个过程之间不需要彼此的返回结果 A过程需要1秒钟,B过程需要2秒钟,主线程其他操作2秒钟按照正常编写,程序大概需要执行5秒如果按照Future模式只需要执行2秒(取其中运行时间最久的线程的运行时间) Future模式的核心实现在于两个方面 1.多线程运行 主线程采用多线的方

Java之多线程中的Future模式

应用场景:线程A需要线程B的执行结果,但没必要一直等待线程B执行完,这个时候可以先拿到未来的Future对象,等线程B执行完再来取真实结果. 定义RealData真实数据类,其构造函数很慢,是用户最后需要使用的数据, static class RealData<T> { protected T result; public RealData(T result) { this.result = result; } public T getResult() { return result; } }

多线程手写Future模式

future模式 在进行耗时操作的时候,线程直接阻塞,我们需要优化这样的代码,让他再启动一个线程,不阻塞.可以执行下面的代码. 这个时候我们就用到了未来者模式 future设计类 只有一个方法 public interface Future<T> { T get() throws InterruptedException; } futureTask 类 public interface FutureTask<T> { T call(); } asyncFuture 类是fufure

多线程异步调用之Future模式

一.什么是异步调用 当我们调用一个函数的时候,如果这个函数的执行过程是很耗时的,我们就必须要等待,但是我们有时候并不急着要这个函数返回的结果.因此,我们可以让被调者立即返回,让他在后台慢慢的处理这个请求.对于调用者来说,则可以先处理一些其他事情,在真正需要数据的时候再去尝试获得需要的数据(这个真正需要数据的位置也就是上文提到的阻塞点).这也是Future模式的核心思想:异步调用. 到了这里,你可能会想CountDownLatch不是也可以实现类似的功能的吗?也是可以让耗时的任务通过子线程的方式去