Java多线程技术研究(四)-Callable,Future/FutureTask,及Future设计模式

来源:互联网 发布:房地产动画制作软件 编辑:程序博客网 时间:2024/05/21 21:44

本篇博客Java多线程中另一块重要的内容:Callable,Future,FutureTask,及Future设计模式的模拟实现。
考虑这样一种场景: 网上购物,提交订单后,在收货的这段时间里无需一直在家里等候,可以先干别的事情。类推到程序设计中时,当提交请求时,期望得到答复时,如果这个答复可能很慢。传统的做法一直等待直到收到应答,可能才会去做后续的事情。在Java中提供Callable和Future机制实现了异步调用,即主线程为得到某个答复,创建一个子线程等待该答复,然后主线程在这个过程中干其他事情,办完其它事情后再去获得答复。我们把这种设计称为Future设计模式。
1、Callable和Runnable
在Java多线程技术研究(一) 中,Callable和Runnable一样,都是实现多线程的一种方法,但是Runnable不会返回结果,并且无法抛出带返回结果的异常,而Callable功能更加强大一些,线程执行结束后可以返回结果。这个结果可以通过Future或FutureTask拿到该线程的返回值。
Callable接口call()方法返回值为泛型,可以返回执行结果,并且还可以抛出异常:

public interface Callable<V> {    /**     * Computes a result, or throws an exception if unable to do so.     *     * @return computed result     * @throws Exception if unable to compute a result     */    V call() throws Exception;}

Runnable接口run()方法返回值为void型,显然不可获得其执行结果

publicinterface Runnable {    /**     * When an object implementing interface <code>Runnable</code> is used     * to create a thread, starting the thread causes the object's     * <code>run</code> method to be called in that separately executing     * thread.     * <p>     * The general contract of the method <code>run</code> is that it may     * take any action whatsoever.     *     * @see     java.lang.Thread#run()     */    public abstract void run();}

2、Callable和Future获取线程执行结果
Future接口中定义有如下方法:
1)cancel()

boolean cancel(boolean mayInterruptIfRunning);

取消任务的执行,mayInterruptRunning参数表示是否中断执行中的线程。
2)isCancelled()

 boolean isCancelled();

任务是否已取消,如果在正常完成前已取消返回true。
3)isDone()

 boolean isDone();

任务是否已完成,不管该任务是正常停止,异常导致,主动终止,均返回true。
4)get()

 V get() throws InterruptedException, ExecutionException;

获取异步执行的结果,如果任务没有执行结束,此方法会阻塞直到任务完成。
5) get(long timeout, TimeUnit unit)

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

其中timeout为超时时间,unit为时间单位,如果在阻塞timeout时间,仍没有获得返回结果,get()方法会抛出TimeoutException异常。
下面来看利用Callable和Future获取线程执行结果

package com.wygu.thread.study;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.FutureTask;public class MultiThreadCallable {    public static void main(String []argv){        TaskCallable tCallable = new TaskCallable();        ExecutorService executorService = Executors.newSingleThreadExecutor();          //将任务提交到线程池中          Future<Integer> future =executorService.submit(tCallable);          //主线程可以进行其他事情            try {            Integer result = future.get();            System.out.println("子线程返回结果为:"+result);        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        }    }}class TaskCallable implements Callable<Integer>{    @Override    public Integer call(){        int num=10;        try {            Thread.sleep(3000);             num+=10;        } catch (InterruptedException e) {            e.printStackTrace();        }       return num;    }}

子线程返回结果为:20

3、Callable和FutureTask
首先看一下类FutureTask的实现:

public class FutureTask<V> implements RunnableFuture<V>

FutureTask实现了接口RunnableFuture,而对于RunnableFuture的实现:

public interface RunnableFuture<V> extends Runnable, Future<V>

RunnableFuture不仅实现了Future中的方法,还实现了Runnable中run方法。
因而FutureTask不仅可以直接提交给Executor执行,还可以调用线程直接执行FutureTask中实现的run()方法.
通过FutureTask的下述两种构造方法,利用FutureTask可以获得Runnable的中执行结果。

    public FutureTask(Callable<V> callable) {        if (callable == null)            throw new NullPointerException();        this.callable = callable;        this.state = NEW;       // ensure visibility of callable    }    public FutureTask(Runnable runnable, V result) {        this.callable = Executors.callable(runnable, result);        this.state = NEW;       // ensure visibility of callable    }

下面来看利用Callable和FutureTask获取线程执行结果

public class MultiThreadCallable {    public static void main(String []argv){        TaskCallable tCallable = new TaskCallable();         FutureTask<Integer> futureTask = new FutureTask<Integer>(tCallable);         new Thread(futureTask,"带返回值的线程").start();         //主线程可以进行其他事情          try {            Integer result = futureTask.get();            System.out.println("子线程返回结果为:"+result);        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        }    }}class TaskCallable implements Callable<Integer>{    @Override    public Integer call(){        int num=10;        try {            Thread.sleep(3000);             num+=10;        } catch (InterruptedException e) {            e.printStackTrace();        }       return num;    }}

程序执行结果:
子线程返回结果为:20

4、Future设计模式的模拟实现
在前面的场景中:网上挑好物品后,提交表单会获得一个订单,然后就可以等待收货,在收货的这段时间里无需一直在家里等候,可以先干别的事情。
这里写图片描述
图片来源于:http://blog.csdn.net/ghuil/article/details/41048017
在传统多线程模式,采用的是同步调用方式,Client发出call请求后,会一直等待服务端给予真实的应答数据,只有当Client等待到应答后,才能进行其它事情。而在Future设计模式调用中,Client发出call请求后,Server端会立即给予Client端一个应答,此时的应答数据可以说是一个假数据(不是真实的结果),Client端收到Server端的应答后,就去处理其它事情了,而Server端内部可以慢慢的生产数据,完成后把放置在对象中,等待Client端获取。
下面给出Future模式的程序实现
1)FutureClient的实现
FutureClient主要功能包括:获得假数据FutureData,获得真实数据RealData

package com.wygu.futurePattern;public class FutureClient<T> {    private T requestData = null;    public FutureClient(T requestdata){        this.requestData = requestdata;    }    //创建方法,获得真实的数据    public FutureData<T> callForBack(){        final FutureData<T> fData = new FutureData<T>();        new Thread(new Runnable() {            @Override            public void run() {                RealData<T> realData = new RealData<T>(requestData);                fData.setRealData(realData);            }        }).start();        return fData;    }}

2)Data的实现
接口Data中,定义一个获得数据的方法getResult(),在FutureData,RealData均重新实现该接口。其中FutureData可以说是对RealData的代理,它会封装RealData的getResult()方法。
Data接口定义

package com.wygu.futurePattern;public interface Data<T> {    public T getResult()  throws InterruptedException;}

FutureData的实现

package com.wygu.futurePattern;public class FutureData<T> implements Data<T>{    private RealData<T> realData = null;    private volatile boolean isReady = false;    public synchronized void setRealData(RealData<T> realData) {        //判断真实数据是否已完成        if(isReady){            return;        }        this.realData = realData;        //数据已生产好        this.isReady = true;        //唤醒等待的线程        notifyAll();    }    @Override    public synchronized T getResult() throws InterruptedException {        if(!isReady){            wait();//阻塞调用该方法的线程,产生真实的数据        }        return realData.getResult();    }}

RealData的实现

package com.wygu.futurePattern;@SuppressWarnings("rawtypes")public class RealData<T> implements Data{    private T realData = null;    public RealData(T realData) {        //使用sleep模拟生产真实数据很慢        try {            Thread.sleep(10000);        } catch (InterruptedException e) {            e.printStackTrace();        }        this.realData = realData;    }    @Override    public T  getResult() throws InterruptedException{          //获取生产的真实数据        return realData;    }}

3)测试运行

package com.wygu.futurePattern;public class Main {    public static void main(String[] args) {        long start = System.currentTimeMillis();        FutureClient<String> futureClient = new FutureClient<String>("Hi,Future Pattern");        //主线程创建Client实例,去请求真实数据        FutureData<String> futureData = futureClient.callForBack();        //让主线程沉睡一段时间,在去获得真实数据        try {            Thread.sleep(9000);        } catch (InterruptedException e) {            e.printStackTrace();        }        try {            System.out.println("Client 获取真实数据:"+futureData.getResult()+",共耗时:"+(System.currentTimeMillis()-start)+"毫秒");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

运行结果为:Client 获取真实数据:Hi,Future Pattern,共耗时:9997毫秒
修改主线程做其他工作时间(沉睡时间)为Thread.sleep(11000),运行结果为:Client 获取真实数据:Hi,Future Pattern,共耗时:10994毫秒。假设主线程的处理其它事物的时间为t1,Server端产生真实数据的时间为t2,则主线程运行结束的时间为max(t1,t2).

阅读全文
0 0