一 .概述
在jdk之中,在1.5之后出现了Future接口,这个接口可以帮助我们完成异步任务结构的获取.
我们首先看下接口的定义:
public interface Future<V> { // 取消任务 boolean cancel(boolean mayInterruptIfRunning); // 判断任务是否被删除掉 boolean isCancelled(); // 判断任务是否完成 boolean isDone(); // 获取数据 V get() throws InterruptedException, ExecutionException; // 给出指定的时间,获取数据 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
但是这个接口存在一个问题,就是在获取数据的时候一旦任务没有完成就会阻塞该方法.
这样在一定的程度上,对并发的效果就是一种削弱.
在guava之中,出现了一种事件回调的方式,当我们的线程获取到执行的结果之后,就会自动帮助我们回调方法,这样就减少了我们对Future结构获取数据的阻塞.
public interface ListenableFuture<V> extends Future<V> { void addListener(Runnable listener, Executor executor); }
通过上面的定义,我们ListenableFuture实际上继承了Future接口,并且添加了一个监听器,通过这种方式,我们就可以指定对应的回调.
通过调用ListenableFuture.addListener方法完成这个功能,这个方法使用Runnable实例和ExecutorService对象,ExecutorService可以是初始任务提交的相同Executor实例,或者完全是另外一个ExecutorService实例。
那么,我们怎么创建ListenableFuture对象呢?
我们知道,Future对象一般情况下是由线程执行器对象通过submit()方法提交获得,而ListenableFuture就是通过一个包装的线程执行器创建出来的.
看下面的例子:
public class ListenableFutureTest { public static void main(String[] args) { // 使用MoreExecutors包装器包装一个基本的Executor对象,我们就能获取到一个ListeningExecutorService对象 ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2)); ListenableFuture<String> listenableFuture = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { TimeUnit.SECONDS.sleep(3); return "trek"; } }); // 添加回调 listenableFuture.addListener(new Runnable() { @Override public void run() { System.out.println("执行了回调的方法"); executorService.shutdown(); } }, executorService); System.out.println("主线程执行完毕"); } }
我们可以发现存在一个问题,那就是我们无法从ListenableFuture之中获取线程任务执行的结果.但是我们可以在线程任务执行完之后,进行另外一个线程任务的回调.