Guava 学习

Guava 学习

学习链接: Google Guava

一、概述

Guava是由Google开发的基于Java的开源库,包含许多Google核心库,它有助于最佳编码实践,并有助于减少编码错误。它为集合[collections]、缓存[caching]、原生类型支持[primitives support]、并发库[concurrency libraries]、通用注解[common annotations]、字符串处理[string processing]、I/O等等提供实用程序方法。

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>${laster.version}</version>
</dependency>

源码包的简单说明

包路径描述
com.google.common.annotations普通注解类型
com.google.common.base基本工具类和接口
com.google.common.cache缓存工具包,非常简单易用且功能强大的JVM内缓存
com.google.common.collection带范型的集合接口扩展和实现,以及工具类,这里你会发现很多好玩的集合
com.google.common.eventbus发布订阅风格的事件总线
com.google.common.hash哈希工具包
com.google.common.ioI/O工具包
com.google.common.math原始算术类型和超大数的运算工具包
com.google.common.net网络工具包
com.google.common.primitives八种原始类型和无符号类型的静态工具包
com.google.common.util.concurrent多线程工具包
二、使用手册
2.1 基本工具[Basic utilities]
2.1.1、使用和避免null

到目前为止,臭名昭著的 NullPointerException 是导致Java应用程序失败的最常见原因。通过查看Google底层代码库,我们发现95%的集合类不接受null值作为元素。我们认为, 相比默默地接受null,使用快速失败操作拒绝null值对开发者更有帮助。此外,Null的含糊语义让人很不舒服。Null很少可以明确地表示某种语义,例如,Map.get(key)返回Null时,可能表示map中的值是null,亦或map中没有key对应的值。Null可以表示失败、成功或几乎任何情况。使用Null以外的特定值,会让你的逻辑描述变得更清晰。鉴于这些原因,很多Guava工具类对Null值都采用快速失败操作,除非工具类本身提供了针对Null值的因变措施。

大多数情况下,开发人员使用null表明的是某种缺失情形,可能是已经有一个默认值,或没有值,或找不到值。例如,Map.get返回的null就表示找不到给定键对应的值。Guava用Optional表示可能为null的T类型引用,一个Optional实例可能包含非null的引用,也可能什么都不包含。它从不说包含的是null值,而是用存在或缺失来表示,但Optional从不包含null值引用。

创建Optional实例(以下都是静态方法):

方法描述
Optional.of(T)创建指定引用的Optional实例,若饮用为null则快速失败
Optional.absent()创建引用缺失的Optional实例
Optional.fromNullable(T)创建指定引用的Optional实例,若引用为null则表示缺失

用Optional实例查询引用(以下都是非静态方法)

方法描述
boolean isPresent如果Optional包含非null的引用(引用存在),返回true
T get()返回Optional所包含的引用,若饮用缺失,则抛出java.lang.IllegalStateException
T or(T)返回Optional所包含的引用,若引用缺失,返回指定的值
T orNull返回Optional所包含的引用,若引用缺失,返回null
Set asSet()返回Optional 所包含引用的单例不可变集,如果引用存在,返回一个只有单一元素的集合,如果引用缺失,返回一个空集合

余下的省略 具体内容见上述链接

2.2 google Guava cache 缓存基本使用讲解

学习链接:google guava cache缓存基本使用讲解

概念

guava cache 是google guava 中的一个内存缓存模块,用于将数据缓存到JVM内存中,实际项目开发中经常将一些比较公共或者常用的数据缓存起来方便快速访问。内存缓存最常见的就是基于HashMap实现的缓存,为了解决并发问题可能也会用到ConcurrentHashMap等并发集合,但是内存缓存需要考虑很多问题,包括并发问题、缓存过期机制、缓存移除机制、缓存命中统计率等。

GuavaCache是google开源java类库Guava的其中一个模块,在maven工程下使用可在pom文件加入如下依赖:

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>

Cache接口及其实现
先说说一般的cache都会实现的基础功能包括:
提供一个存储缓存的容器,该容器实现了存放(Put)和读取(Get)缓存的接口供外部调用。缓存通常以<key,value>的形式存在,通过key来从缓存中获取value。当然容器的大小往往是有限的(受限于内存大小),需要为它设置清除缓存的策略。

在GuavaCache中缓存的容器被定义为接口Cache<K,V>的实现类,这些实现类都是线程安全的,因此通常定义为一个单例。并且接口Cache是范型,很好的支持了不同类型的key和value。作为示例,我们构建一个key为Integer、value为String的Cache实例:

	final static Cache<Integer, String> cache = CacheBuilder.newBuilder()
			//设置cache的初始大小为10,要合理设置该值
			.initialCapacity(10)
			//设置并发数为5,即同一时间最多只能有5个线程往cache执行写入操作
			.concurrencyLevel(5)
			//设置cache中的数据在写入之后的存活时间为10秒
			.expireAfterWrite(10, TimeUnit.SECONDS)
			//构建cache实例
			.build();

据说GuavaCache的实现是基于ConcurrentHashMap的,因此上面的构造过程所调用的方法,通过查看其官方文档也能看到一些类似的原理,比如通过initialCapacity(5) 定义初始值的大小,要是定义太大就会浪费内存空间,要是太小,需要扩容的时候就会像map一样需要resize,这个过程会产生大量需要gc的对象,还有比如通过concurrencyLevel(5)来限制写入操作的并发数,这和ConcurrentHashMap的锁机制也是类似的(ConcurrentHashMap读不需要加锁,写入需要加锁,每个segment都有一个锁)。

接下来看看Cache提供哪些方法(只列了部分常用的):

/**
 * 该接口的实现被认为是线程安全的,即可在多线程中调用
 * 通过被定义单例使用
 */
public interface Cache<K, V> {
 
  /**
   * 通过key获取缓存中的value,若不存在直接返回null
   */
  V getIfPresent(Object key);
 
  /**
   * 通过key获取缓存中的value,若不存在就通过valueLoader来加载该value
   * 整个过程为 "if cached, return; otherwise create, cache and return"
   * 注意valueLoader要么返回非null值,要么抛出异常,绝对不能返回null
   */
  V get(K key, Callable<? extends V> valueLoader) throws ExecutionException;
 
  /**
   * 添加缓存,若key存在,就覆盖旧值
   */
  void put(K key, V value);
 
  /**
   * 删除该key关联的缓存
   */
  void invalidate(Object key);
 
  /**
   * 删除所有缓存
   */
  void invalidateAll();
 
  /**
   * 执行一些维护操作,包括清理缓存
   */
  void cleanUp();
}

使用过程还是要认真查看官方的文档,以下Demo简单的展示了Cache的写入,读取,和过期清除策略是否生效:

	public static void main(String[] args) throws Exception {
		cache.put(1, "Hi");
		
		for(int i=0 ;i<100 ;i++) {
			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
			System.out.println(sdf.format(new Date()) 
					+ "  key:1 ,value:"+cache.getIfPresent(1));
			Thread.sleep(1000);
		}
	}

清除缓存的策略

任何Cache的容量都是有限的,而缓存清除策略就是决定数据在什么时候应该被清理掉,GuavaCache提了以下几种清除策略

基于存活时间的清除(Timed Eviction)
这应该是最常见的清除策略,在构建Cache实例的时候,CacheBuilder提供两种基于存活时间的构建方法:
(1)expireAfterAccess(long,TimeUnit): 缓存项在创建后,在给定的时间内没有被读/写访问,则清除
(2)expireAfterWrite(long,TimeUnit): 缓存项在创建后,在给定的时间内没有被写访问(创建或覆盖),则清除
expireAfterWrite()方法有些类似于redis中的expire命令,但显然它只能设置所有缓存具有相同的存活时间,若遇到一些缓存数据的存活时间为1分钟,一些为5分钟,那只能构建两个Cache实例了。

基于容量的清除(size-base eviction)
在构建Cache实例的时候,通过CacheBuilder.maximumSize(long)方法可以设置Cache的最大容量数,当缓存数量达到或接近该最大值时,Cache将清除掉那些最近最少使用的缓存。
以上这种方式是以缓存的"数量"作为容量的计算方式,还有另外一种基于"权重"的计算方式。比如每一项缓存所占据的内存空间大小都不一样,可以看作它们有不同的"权重"(weights)。你可以使用CacheBuilder.weigher(Weigher)指定一个权重函数,并且用CacheBuilder.maximumWeight(long) 指定最大总重。

未完待续。。。

2.3 google Google com.google.common.base包的使用

学习链接:Guava笔记之Supplier和Suppliers的使用

2.3.1、Supplier 和 Suppliers的使用

Supplier也是用来创建对象的,但是不同于传统的创建对象的语法: new

Supplier封装了获取值的功能,可以实现多种不同的获取值的方式,创建Supplier可以使用Suppliers类的静态方法来实现,该方法提供了多种获取值的方式,具体为:
创建supplier的方式
一、supplier接口
该接口提供了get方法用于获取值,注意其中的get方法并不能传递参数信息,所以如果需要传递信息的话则不适用该接口。

public interface Supplier<T> extends java.util.function.Supplier<T> {
  T get();
}

二、实例化Supplier的方式

1、Suppliers.ofInstance(T instance) 方法

该种调用方式为最简单的实现方式,在调用get方法的时候会直接将传递的instance返回

public static <T> Supplier<T> ofInstance(@Nullable T instance) {
   return new SupplierOfInstance<T>(instance);
 }
 
private static class SupplierOfInstance<T> implements Supplier<T>, Serializable {
    final @Nullable T instance;
    SupplierOfInstance(@Nullable T instance) {
      this.instance = instance;
    }
    @Override
    public T get() {
      return instance;
    }
  }

2、Suppliers.memoizeWithExpiration(Supplier delegate,long duration,TimeUnit unit)方法
该方法会根据指定的durationunit每隔指定时间调用delegateget方法一次,相当于是定时刷新结果值

public static <T> Supplier<T> memoizeWithExpiration(
      Supplier<T> delegate, long duration, TimeUnit unit) {
    return new ExpiringMemoizingSupplier<T>(delegate, duration, unit);
}

// 具体的ExpiringMemoizingSupplier实现
static class ExpiringMemoizingSupplier<T> implements Supplier<T>, Serializable {
    final Supplier<T> delegate; // 具体获取结果的代理类
    final long durationNanos; // 刷新结果间隔的纳秒值
    transient volatile @Nullable T value; // 缓存的结果信息
    transient volatile long expirationNanos; // 0表示还没有初始化 否则表示下一次过期的时间
    
    ExpiringMemoizingSupplier(Supplier<T> delegate, long duration, TimeUnit unit) {
      this.delegate = checkNotNull(delegate);
      this.durationNanos = unit.toNanos(duration);
      checkArgument(duration > 0, "duration (%s %s) must be > 0", duration, unit);
    }

    @Override
    public T get() {
      long nanos = expirationNanos;
      long now = Platform.systemNanoTime();
      if (nanos == 0 || now - nanos >= 0) {
        synchronized (this) {
          if (nanos == expirationNanos) {      // 双重检查防止重复加载
            T t = delegate.get();
            value = t;
            nanos = now + durationNanos;
            expirationNanos = (nanos == 0) ? 1 : nanos;
            return t;
          }
        }
      }
      return value;
    }
}

可以使用以上的方法封装一个缓存类

将该方法使用@PostConstruct修饰 每次调用该缓存类的对象时都会进行缓存的更新。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值