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

上面两幅图分别说明了传统的同步方法和Future模式的同步方法.
Future 模式的主要参与者:

JDK中的Future 模式

Future 接口表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消里面的get()方法的行为取决于任务的状态,完成时会返回值或者抛出异常,如果任务没有完成,任务会阻塞直到完成, RunnableFuture 继承了Future和Runnable两个接口,其中,run()方法用于构造真实的数据.它有一个具体的实现类FutureTask, FutureTask 有一个内部类Sync,一些实质性的工作,会委托Sync完成.而Sync 最终会调用Callable接口,完成实际数据的组装工作.
Callable接口类似于Runnable,但是Runnable不会返回结果,并且无法抛出返回结果的异常,并且无法抛出返回结果的异常,而Callable工程更强大一点,被线程执行后,可以返回值,这个返回值可以被future拿到,也就是说,future可以拿到异步执行任务的返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class FutureCallableDemo { public static void main(String[] args) throws Exception { Callable<String> realData =new RealData("a");
ExecutorService es = Executors.newFixedThreadPool(1); Future<String> future = es.submit(realData); System.out.println(future.get()); } static class RealData implements Callable<String> { private String para;
public RealData(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(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 一个使用Callable和future的读网页的例子,图片另外一个线程读,文字直接主线程读。 public class FutureRender { private final ExcutorService excutor = newFixedThreadPool(10); void renderPage(CharSequence source) { final List<ImageInfo> ImageInfos =scanforImageInfo(source); Callable<List<ImageData> > task =new Callable<>() { public List<ImageData> call() { List<ImageData> result =new ArrayList<ImageData>(); for(ImageInfo imageinfo: iamgeinfos) { results.add(imageinfo.download()); } return results; } };
Future<List<ImageData> > future = excutor.submit(task); renderText(source); try{ List<ImageData> imageData =future.get(); for(ImageData data :imageData) { renderImage(data); } } catch(InterruptedException e) { Thread.currentThread().interrupt); future.cancel(true); } catch(ExecutionException e) { throw launderThrowable(e.getCause()); } } }
|
FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到。