Guava-Java核心工具库(二)

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);
MethodDescriptionSee 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.NEWService.State.STARTINGService.State.RUNNINGService.State.STOPPINGService.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() 。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨路行人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值