背景
先给你一张凭据
假设有个任务需要执行比较长的时间,通常需要等待任务执行结束或者出错才能返回结果,在此期间调用者只能陷入阻塞苦苦等待,对此,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方法获得最终的计算结果。 * */ } }