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

被折叠的 条评论
为什么被折叠?



