Future接口
Future表示一个异步计算任务的结果。
Future接口方法有:
boolean cancel(boolean mayInterruptIfRunning):取消异步运算。入参为true时,表示会取消已经开始执行的运算。为false时,不会取消已经开始执行的运算。
boolean isCancelled():
boolean isDone():任务结束的话,返回true,否则返回false。任务结束不仅仅指任务顺利完成,还包括任务抛异常、任务被取消等。
V get() throws InterruptedException, ExecutionException:在主线程中获取异步运算的结果,在获取到结果之前主线程一直阻塞。如果任务执行抛异常,则get()方法会抛出ExecutionException异常。如果任务被取消,则get()会抛出CancellationException异常。
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException:在主线程中获取异步运算的结果,在获取到结果之前主线程阻塞,超过指定时间后会抛出TimeoutException异常。
RunnableFuture接口
RunnableFuture接口继承了Runnable接口和Future接口,但并未定义自己的方法。
FutureTask是RunnableFuture实现类。FutureTask有两个构造器:FutureTask(Callable<V> callable)和FutureTask(Runnable runnable, V result)。
TODO示例
FutureTask的run()实例方法内部是调用Runnable实例或者Callable实例的run()方法,但是是在主线程执行,而不是在异步线程执行。这j8有啥用???
ListenableFuture接口
其实ListenableFuture接口有两个,但都不是jdk自带的,guava一个,spring一个。
guava的ListenableFuture接口全类名是com.google.common.util.concurrent.ListenableFuture,继承了Future接口,只定义了一个方法:void addListener(Runnable listener, Executor executor)。常用实现类是guava的ListenableFutureTask类,全类名是com.google.common.util.concurrent.ListenableFutureTask。
spring的ListenableFuture接口在spring-core.jar包中,全类名是org.springframework.util.concurrent.ListenableFuture,继承了Future接口,定义方法如下:
void addCallback(ListenableFutureCallback<T> callback):注册一个ListenableFutureCallback实例。
void addCallback(SuccessCallback<T> successCallback, FailureCallback failureCallback):注册一个SuccessCallback实例和一个failureCallback实例。
completableFuture<T> completable():把一个spring的ListenableFuture实例转成一个jdk的CompletableFuture实例。
spring的ListenableFuture接口常用实现类是spring的ListenableFutureTask和AsyncResult。
guava ListenableFutureTask使用示例:
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); ListeningExecutorService service = MoreExecutors.listeningDecorator(executorService); long begin = System.currentTimeMillis(); ListenableFuture<Boolean> booleanTask = service.submit(() -> { Thread.sleep(1000); System.out.println("1, currentThread= " + Thread.currentThread().getName()); System.out.println("".substring(0, 1)); return true; }); booleanTask.addListener(() -> { try { booleanTask.get(); Thread.sleep(2000); System.out.println("2, currentThread= " + Thread.currentThread().getName()); } catch (Exception e) { System.out.println(123); // e.printStackTrace(); } }, executorService); Futures.addCallback(booleanTask, new FutureCallback<Boolean>() { @Override public void onSuccess(Boolean result) { System.out.println("3, currentThread= " + Thread.currentThread().getName() + ", booleanTask result= " + result); } @Override public void onFailure(Throwable e) { System.out.println(234); // e.printStackTrace(); } }, executorService); System.out.println("cost " + (System.currentTimeMillis() - begin) + "ms"); }
利用guava的MoreExecutors工具类构造出ListeningExecutorService实例,调用此实例的submit()方法即可得到一个ListenableFuture实例。接下来可以调用ListenableFuture的addListener(Runnable listener, Executor executor)方法添加一个监听任务,但这个方法说实话,不好。为什么呢?因为addListener()方法添加的监听任务不管被监听任务是否抛异常都会执行,要是想不抛异常执行一个任务,抛异常时执行另一个任务,就要用try catch把ListenableFuture实例的get()方法包裹,然后在try子句中写不抛异常时要执行的任务,catch子句中写抛异常时要执行的任务。不如用guava提供的Futures工具类,这个工具类中提供了很多静态方法,其中addCallback(ListenableFuture<V> future, FutureCallback<V> callback, Executor executor)方法可以轻松实现ListenableFuture任务抛异常和不抛异常时监听任务的不同分支。
FluentFuture类
FluentFuture是guava提供的类,在com.google.common.util.concurrent包中,全类名是com.google.common.util.concurrent.FluentFuture。
待研究
spring ListenableFutureTask使用示例:
TODO示例
AsyncResult类
AsyncResult是spring提供的类,在spring-context.jar包中,全类名是org.springframework.scheduling.annotation.AsyncResult。AsyncResult实现了ListenableFuture接口。
AsyncResult使用示例:
public static void main(String[] args) throws NoSuchAlgorithmException { AsyncResult<Number> asyncResult = new AsyncResult<>(getRandom()); asyncResult.addCallback(result -> { System.out.println("result= " + result + ", currentThread= " + Thread.currentThread().getName()); }, e -> { System.out.println("e= " + e.getMessage() + ", currentThread= " + Thread.currentThread().getName()); } ); } private static Double getRandom() throws NoSuchAlgorithmException { SecureRandom secureRandom = SecureRandom.getInstanceStrong(); return secureRandom.nextDouble(); }
这下终于按照预期打印了。
CompletableFuture接口
待研究