目录
【并发编程】
并发编程是一个难题,但是一个强大而简单的抽象可以显著的简化并发的编写。出于这样的考虑,Guava 定义了 ListenableFuture 接口并继承了JDK concurrent包下的 Future 接口。Guava强烈建议在代码中多使用 ListenableFuture 来代替JDK的 Future, 因为:
● 大多数 Futures 方法中需要它。
● 转到 ListenableFuture 编程比较容易。
● Guava提供的通用公共类封装了公共的操作方方法,不需要提供Future和ListenableFuture的扩展方法。
一、Future
在了解 ListenableFuture 之前,我们先对Java的 Future 简单了解下:
Future 是实现Java多线程的方式之一,Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。可配合 ExecutorService、Callable使用。
1.通过 FutureTask 实现
private void FutureTest1() throws Exception {
// 创建单任务线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
// FutureTask 实现
FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
return "def";
}
});
executor.execute(future);
// 获取执行结果并设置超时时间
System.out.println(future.get(200, TimeUnit.MILLISECONDS));
// 关闭线程池
if (!executor.isShutdown()) {
executor.shutdown();
}
}
2.通过 submit() 方法实现
private void FutureTest2() throws Exception {
// 创建单任务线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
// submit() 方法实现
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "abc";
}
});
// 获取执行结果
System.out.println(future.get());
// 关闭线程池
executor.shutdown();
}
补充:
JDK Executors 创建线程池的方法说明
方法 | 说明 |
---|---|
newCachedThreadPool() | 可变尺寸线程池 |
newFixedThreadPool(3) | 固定大小线程池 |
newScheduledThreadPool(2) | 延迟连接池 |
newSingleThreadExecutor() | 单任务线程池 |
newSingleThreadScheduledExecutor() | 单任务延迟连接池 |
newWorkStealingPool() | 并行执行线程池 |
注:可以了解下 Runnable接口 和 Callable接口 的差异。
二、Futures
Guava提供了 FutureCallback 用于对Future的返回结果进行处理。FutureCallback 中实现了两个方法:
● onSuccess(V),在Future成功的时候执行,根据Future结果来判断。
● onFailure(Throwable),在Future失败的时候执行,根据Future结果来判断。
FutureCallback 通过 Futures 添加回调。
Futures 的其他常用工具:
方法 | 说明 |
---|---|
transformAsync(ListenableFuture, AsyncFunction<I, O>, Executor) | 返回一个新的ListenableFuture,该ListenableFuture 返回的result是由传入的AsyncFunction 参数指派到传入的 ListenableFuture中 |
transformAsync(ListenableFuture, AsyncFunction<I, O>,) | 返回一个新的ListenableFuture ,该ListenableFuture 返回的result是由传入的AsyncFunction 参数指派到传入的 ListenableFuture中 |
allAsList(Iterable<ListenableFuture) | 返回一个ListenableFuture ,该ListenableFuture 返回的result是一个List,List中的值是每个ListenableFuture的返回值,假如传入的其中之一fails或者cancel,这个Future fails 或者canceled |
successfulAsList(Iterable<ListenableFuture) | 返回一个ListenableFuture ,该Future的结果包含所有成功的Future,按照原来的顺序,当其中之一Failed或者cancel,则用null替代 |
ListenableFuture<Integer> transform = Futures.transformAsync(future, new AsyncFunction<String, Integer>() {
@Override
public ListenableFuture<Integer> apply(String input) {
// todo 将 ListenableFuture<String> 转为 ListenableFuture<Integer>
return null;
}
});
三、ListenableFuture
ListenableFuture继承了Future,它允许注册回调方法(callbacks),在运算(多线程执行)完成的时候进行调用,或者在运算(多线程执行)完成后立即执行。
ListenableFuture 中的基础方法是addListener(Runnable, Executor),该方法会在多线程运算完的时候,指定的Runnable参数传入的对象会被指定的Executor执行。
对应JDK中的 ExecutorService.submit(Callable) 提交多线程异步运算的方式,Guava 提供了ListeningExecutorService 接口,该接口返回 ListenableFuture 而相应的 ExecutorService 返回普通的 Future。将 ExecutorService 转为 ListeningExecutorService,可以使用MoreExecutors.listeningDecorator(ExecutorService)进行装饰。
ListenableFuture的实现:
private void FutureTest3() throws Exception {
ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
ListenableFuture<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "ghi";
}
});
// 添加回调
Futures.addCallback(future, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println(result);
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
if (!executor.isShutdown()) {
executor.shutdown();
}
}
同理也可使用 ListenableFutureTask 来实现:
private void FutureTest4() throws Exception {
ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
ListenableFutureTask<String> futureTask = ListenableFutureTask.create(new Callable<String>() {
@Override
public String call() throws Exception {
return "jkl";
}
});
executor.execute(futureTask);
System.out.println(futureTask.get());
executor.shutdown();
}
通过加了一层修饰,就可以支持一些其他的操作,而在JDK concurrent中的Future是不行的。