Future设计模式

背景

先给你一张凭据

        假设有个任务需要执行比较长的时间,通常需要等待任务执行结束或者出错才能返回结果,在此期间调用者只能陷入阻塞苦苦等待,对此,Future设计模式提供了一种凭据式的解决方案。在我们日常生活中,关于凭据的使用非常多见,比如你去某西服手工作坊想订做一身合体修身的西服,西服的制作过程比较漫长,少则一个礼拜,多则一个月,你不可能一直待在原地等待,一般来说作坊会为你开一个凭据,此凭据就是Future,在接下来的任意日子里你可以凭借此凭据到作坊获取西服。

        自JDK1.5起,Java提供了比较强大的Future接口,在JDK1.8时更是引入了CompletableFuture,其结合函数式接口可实现更强大的功能。

接口定义

Future接口设计

package MutilThreadModel.FutureModel;
/**
 * Created by JYM on 2019/1/10
 * Future接口设计:
 * Future提供了获取计算结果和判断任务是否完成的两个接口,其中获取计算结果将会导致调用阻塞(在任务还未完成的情况下)
 * */

public interface Future<T>
{
    //返回计算后的结果,该方法会陷入阻塞状态
    T get() throws InterruptedException;

    //判断任务是否已经被执行完成
    boolean done();
}

FutureService接口设计

package MutilThreadModel.FutureModel;
/**
 * Created by JYM on 2019/1/10
 * FutureService主要用于提交任务,提交的任务主要有两种,第一种不需要返回值,第二种则需要获得最终的计算结果。
 * FutureService接口中提供了对FutureServiceImpl构建的工厂方法,JDK8中不仅支持default方法还支持静态方法。
 * JDK9甚至还支持接口私有方法。*/

public interface FutureService<IN,OUT>
{
    //提交不需要返回值的任务,Future.get方法返回的将会是null
    Future<?> submit(Runnable runnable);

    //提交需要返回值的任务,其中Task接口代替了Runnable接口
    Future<OUT> submit(Task<IN,OUT> task,IN input);

    //增加回调机制
    Future<OUT> submit(Task<IN,OUT> task,IN input,Callback<OUT> callback);

    //使用静态方法创建一个FutureService的实现
    static <T,R> FutureService<T,R> newService()
    {
        return new FutureServiceImpl<>();
    }
}

Task接口设计

package MutilThreadModel.FutureModel;
/**
 * Created by JYM on 2019/1/10
 * Task接口设计:
 * Task接口主要是提供给调用者实现计算逻辑之用的,可以接受一个参数并且返回最终的计算结果。
 * */

@FunctionalInterface
public interface Task<IN, OUT>
{
    //给定一个参数,经过计算返回结果
    OUT get(IN input);
}

程序实现

1、FutureTask:

package MutilThreadModel.FutureModel;
/**
 * Created by JYM 2019/1/10
 * FutureTask是Future的一个实现,除了实现Future中定义的get()以及done()方法,还额外增加了
 * protected方法finish,该方法主要用于接受任务被完成的通知。
 * */

public class FutureTask<T> implements Future<T>
{
    //计算结果
    private T result;

    //任务是否完成
    private boolean isDone = false;

    //定义对象锁
    private final Object LOCK = new Object();

    @Override
    public T get() throws InterruptedException
    {
        synchronized (LOCK)
        {
            //当任务还没完成时,调用get方法会被挂起而进入阻塞
            while (!isDone)
            {
                LOCK.wait();
            }
            //返回计算结果
            return result;
        }
    }
    //finish方法主要用于为FutureTask设置计算结果
    protected void finish(T result)
    {
        synchronized (LOCK)
        {
            //balking设计模式
            if (isDone)
            {
                return;
            }
            //计算完成,为result指定结果,并且将isDone设为true,同时唤醒阻塞中的线程
            this.result = result;
            this.isDone = true;
            LOCK.notifyAll();
        }
    }
    //返回当前任务是否已经完成
    @Override
    public boolean done() {
        return isDone;
    }
}
/**
 * FutureTask中充分利用了线程间的通信wait和notifyAll,当任务没有被完成之前通过get方法获取结果,调用者会进入阻塞,
 * 直到任务完成并接收到其他线程的唤醒信号,finish方法接收到了任务完成通知,唤醒了因调用get而进入阻塞的线程。
 * */

2、FutureServiceImpl

package MutilThreadModel.FutureModel;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * FutureServiceImpl的主要作用在于当提交任务时创建一个新的线程来受理该任务,
 * 进而达到任务异步执行的效果。
 * */

public class FutureServiceImpl<IN,OUT> implements FutureService<IN, OUT>
{
    //为执行的线程指定名字前缀(再三强调,为线程起一个特殊的名字是一个非常好的编程习惯)
    private final static String FUTURE_THREAD_PREFIX = "FUTURE-";

    private final AtomicInteger nextCounter = new AtomicInteger(0);

    private String getNextName()
    {
        return FUTURE_THREAD_PREFIX+nextCounter.getAndIncrement();
    }

    @Override
    public Future<?> submit(Runnable runnable) {
        final FutureTask<Void> futureTask = new FutureTask<>();
        new Thread(()->
        {
            runnable.run();
            //任务执行结束之后将null作为结果传给futureTask
            futureTask.finish(null);
        },getNextName()).start();

        return futureTask;
    }

    @Override
    public Future<OUT> submit(Task<IN, OUT> task, IN input) {
        final FutureTask<OUT> future = new FutureTask<>();
        new Thread(()->
        {
            OUT result = task.get(input);
            //任务执行结束之后,将真实的结果通过finish方法传递给future
            future.finish(result);
        },getNextName()).start();

        return future;
    }

//下面是增强FutureService使其支持回调
    /*
    * 使用任务完成时回调的机制可以让调用者不再进行显式地通过get的方式获得数据而导致进入阻塞,可在提交任务的时候将回调接口一并注入。
    * */
    @Override
    public Future<OUT> submit(Task<IN, OUT> task, IN input, Callback<OUT> callback)
    {
        final FutureTask<OUT> future = new FutureTask<>();
        new Thread(()->{
            OUT result = task.get(input);
            future.finish(result);
            //执行回调接口
            if (null!=callback)
            {
                callback.call(result);
            }
        },getNextName()).start();

        return future;
    }
    /**
     * 修改后的submit方法,增加了一个Callback参数,主要用来接受并处理任务的计算结果,当提交的任务执行完成之后,会将结果传递
     * 给Callback接口进行进一步的执行,这样在提交任务之后不再会因为通过get方法获得结果而陷入阻塞。
     * */
}

/**
 * 在FutureServiceImpl的submit方法中,分别启动了新的线程运行任务,起到了异步作用,在任务最终运行成功之后,会通知FutureTask任务已经完成
 * */

回调接口Callback:

package MutilThreadModel.FutureModel;
/**
 * Created by JYM on 2019/1/10
 * 回调接口
 * */

public interface Callback<T>
{
    //任务完成后调用该方法,其中T为任务执行后的结果
    void call(T t);
}

测试程序:

package MutilThreadModel.FutureModel;

import java.util.concurrent.TimeUnit;

public class test
{
//    public static void main(String[] args) throws InterruptedException
//    {
//        //定义不需要返回值的FutureService
//        FutureService<Void,Void> service = FutureService.newService();
//        //submit方法为立即返回的方法
//        Future<?> future = service.submit(()->
//        {
//            try{
//                TimeUnit.SECONDS.sleep(10);
//            }catch (InterruptedException e)
//            {
//                e.printStackTrace();
//            }
//            System.out.println("I am finish done.");
//        });
//
//        //get方法会使当前线程进入阻塞
//        future.get();
//    }

//    public static void main(String[] args) throws InterruptedException
//    {
//        //定义有返回值的FutureService
//        FutureService<String,Integer> service = FutureService.newService();
//        //submit方法会立即返回
//        Future<Integer> future = service.submit(input -> {
//            try{
//                TimeUnit.SECONDS.sleep(10);
//            }catch (InterruptedException e)
//            {
//                e.printStackTrace();
//            }
//            return input.length();
//        },"Hello");
//
//        //get方法使当前线程进入阻塞,最终会返回计算的结果
//        System.out.println(future.get());
//    }

    /**下面是对增加了Callback之后的Future任务提交之后的测试*/
    public static void main(String[] args) throws InterruptedException
    {
        FutureService<String,Integer> service = FutureService.newService();
        service.submit(input -> {
            try{
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            return input.length();
        },"Hello",System.out::println);

        /*
        * 此处的System.out::println是一个Lambda表达式的静态推导,其作用就是实现call方法,通过升级后的程序你会发现,我们
        * 再也不需要通过get的方式来获得结果了,当然你也可以继续使用get方法获得最终的计算结果。
        * */
    }
}

猜你喜欢

转载自blog.csdn.net/leying521/article/details/86300538