目录
guava
最近在公司的项目中注意到了一个依赖guava,在项目中很多地方都用到了它,特别是集合转换时大量使用,而Guava的通用性和实用性相信每一个javaer都有所体会,今天就来介绍一下这款来自Google的Java核心工具库。
官方是这样介绍Guava
Guava is a set of core Java libraries from Google that includes new collection types (such as multimap and multiset), immutable collections, a graph library, and utilities for concurrency, I/O, hashing, primitives, strings, and more! It is widely used on most Java projects within Google, and widely used by many other companies as well.
翻译过来就是,Guava 是 Google 的一组核心 Java 库,其中包括新的集合类型(例如 multimap 和 multiset)、不可变集合、图形库以及用于并发、I/O、哈希、原语、字符串等的实用程序!它被广泛用于 Google 内部的大多数 Java 项目,也被许多其他公司广泛使用。
缓存
本地缓存,支持各种过期行为。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
@Override
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
实用工具
谨慎使用Guava的函数式习语可以显著简化代码。函数式编程和Java 8+的函数式编程类似,建议了解即可,不要混淆
并发
ListenableFuture
并发是一个难题,但通过使用强大而简单的抽象可以大大简化它。为了简化问题,Guava 使用 ListenableFuture 扩展了 JDK 的 Future 接口。
传统的Future表示异步计算的结果:一种可能已经或可能尚未完成生成结果的计算。Future可以是正在进行的计算的句柄,是服务向我们提供结果的承诺。
ListenableFuture 允许您注册在计算完成后要执行的回调,或者如果计算已经完成,则立即执行。这个简单的添加使得高效支持基本 Future 接口无法支持的许多操作成为可能。
ListenableFuture 添加的基本操作是 addListener(Runnable, Executor),它指定当这个 Future 表示的计算完成后,指定的 Runnable 将运行在指定的 Executor 上。
这里关于Future的基本概念和用法,可参照之前的一篇 Netty详解(二):异步回调Future介绍 文章,里面详细介绍了JDK的Future和Netty的Future,而Guava中的ListenableFuture和Netty中的Future在部分有异曲同工之妙。
FutureCallback 实现两个方法:
- onSuccess(V),如果ListenableFuture计算成功,则根据其结果执行的操作
- onFailure(Throwable),如果ListenableFuture计算失败,根据失败执行的操作
与 JDK ExecutorService.submit(Callable) 启动异步计算的方法相对应,Guava 提供了 ListeningExecutorService 接口,该接口在 ExecutorService 返回普通 Future 的地方返回 ListenableFuture。要将 ExecutorService 转换为 ListeningExecutorService,只需使用 MoreExecutors.listeningDecorator(ExecutorService) 即可。
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture<Explosion> explosion = service.submit(
new Callable<Explosion>() {
public Explosion call() {
return pushBigRedButton();
}
});
Futures.addCallback(
explosion,
new FutureCallback<Explosion>() {
// we want this handler to run immediately after we push the big red button!
public void onSuccess(Explosion explosion) {
walkAwayFrom(explosion);
}
public void onFailure(Throwable thrown) {
battleArchNemesis(); // escaped the explosion!
}
},
service);
或者,如果您从基于 FutureTask 的 API 进行转换,Guava 提供 ListenableFutureTask.create(Callable) 和 ListenableFutureTask.create(Runnable, V)。与 JDK 不同,ListenableFutureTask 并不意味着可以直接扩展。
如果你必须将另一个API提供的Future转换为ListenableFuture,你可能别无选择,只能使用重量级的JdkFutureAdapters.listenInPoolThread(Future)将Future转换为ListenableFuture。只要有可能,最好修改原始代码以返回 ListenableFuture。
使用 ListenableFuture 的最重要原因是可以拥有复杂的异步操作链。
ListenableFuture<RowKey> rowKeyFuture = indexService.lookUp(query);
AsyncFunction<RowKey, QueryResult> queryFunction =
new AsyncFunction<RowKey, QueryResult>() {
public ListenableFuture<QueryResult> apply(RowKey rowKey) {
return dataService.read(rowKey);
}
};
ListenableFuture<QueryResult> queryFuture =
Futures.transformAsync(rowKeyFuture, queryFunction, queryExecutor);
Method | Description | See also |
---|---|---|
transformAsync(ListenableFuture, AsyncFunction, Executor) * | 返回一个新的 ListenableFuture ,其结果是将给定的 AsyncFunction 应用于给定 ListenableFuture 的结果的产物。 | transformAsync(ListenableFuture, AsyncFunction) |
transform(ListenableFuture, Function, Executor) | 返回一个新的 ListenableFuture ,其结果是将给定 Function 应用于给定 ListenableFuture 的结果的产物。 | transform(ListenableFuture, Function) |
allAsList(Iterable>) | 返回一个 ListenableFuture ,其值是一个列表,其中按顺序包含每个输入 future 的值。如果任何输入Future失败或被取消,则该Future也会失败或被取消。 | allAsList(ListenableFuture...) |
successfulAsList(Iterable>) | 返回一个 ListenableFuture ,其值是一个列表,其中按顺序包含每个成功输入 future 的值。与失败或取消的 future 对应的值将替换为 null。 | successfulAsList(ListenableFuture...) |
Service
Service 的正常生命周期:
Service.State.NEW、Service.State.STARTING、Service.State.RUNNING、Service.State.STOPPING、Service.State.TERMINATED
已停止的Service可能无法重新启动。如果Service在启动、运行或停止时失败,它将进入 Service.State.FAILED 状态。
如果Service是Service.State.NEW的,则可以使用 startAsync() 异步启动Service。因此,您应该构建您的应用程序,让每个Service都有一个独特的启动位置。
Service接口微妙而复杂。我们不建议直接实施它。相反,请使用 guava 中的抽象基类之一作为您的实现的基础。每个基类都支持特定的线程模型。
Service的抽象实现:
-
AbstractIdleService
AbstractIdleService 框架实现了一个服务,该服务在“运行”状态下不需要执行任何操作,因此在运行时不需要线程,但需要执行启动和关闭操作。
protected void startUp() { servlets.add(new GcStatsServlet()); } protected void shutDown() {}
-
AbstractExecutionThreadService
AbstractExecutionThreadService 在单个线程中执行启动、运行和关闭操作。您必须重写 run() 方法,并且它必须响应停止请求。例如,您可以在工作循环中执行操作
public void run() { while (isRunning()) { // perform a unit of work } }
重写 startUp() 和 shutdown() 是可选的,但服务状态将为您管理
protected void startUp() { dispatcher.listenForConnections(port, queue); } protected void run() { Connection connection; while ((connection = queue.take() != POISON)) { process(connection); } } protected void triggerShutdown() { dispatcher.stopListeningForConnections(queue); queue.put(POISON); }
请注意,start() 调用您的 startUp() 方法,为您创建一个线程,并在该线程中调用 run()。 stop() 调用triggerShutdown() 并等待线程终止。
-
AbstractScheduledService
AbstractScheduledService 在运行时执行一些周期性任务。子类实现 runOneIteration() 来指定任务的一次迭代,以及熟悉的 startUp() 和 shutdown() 方法。
为了描述执行计划,你必须实现scheduler()方法。通常,您将使用 AbstractScheduledService.Scheduler 提供的调度方式之一,即 newFixedRateSchedule(initialDelay, Delay, TimeUnit) 或 newFixedDelaySchedule(initialDelay, Delay, TimeUnit),对应于 ScheduledExecutorService 中熟悉的方法。可以使用CustomScheduler实现自定义调度。
-
AbstractService
当你需要自己手动进行线程管理时,直接重写AbstractService。通常,上述实现之一应该可以很好地满足您的需求,但建议您实现 AbstractService,例如,当您正在建模提供自己的线程语义作为服务的东西时,您有自己的特定线程需求。
要实现 AbstractService,您必须实现 2 个方法:
-
doStart():doStart() 是由第一次调用 startAsync() 直接调用的,您的 doStart() 方法应该执行所有初始化,然后如果启动成功则最终调用 notificationStarted() ,如果启动失败则最终调用 notificationFailed() 。
-
doStop():doStop() 是由第一次调用 stopAsync() 直接调用的,您的 doStop() 方法应该关闭您的服务,然后最终在关闭成功时调用 notificationStopped() ,或者在关闭失败时调用 notificationFailed() 。
-