简介

guava是google的一个开源java框架,其github地址是 https://github.com/google/guava。guava工程包含了若干被Google的Java项目广泛依赖的核心库,例如:

  • 集合 [collections]
  • 缓存 [caching]
  • 原生类型支持 [primitives support]
  • 并发库 [concurrency libraries]
  • 通用注解 [common annotations]
  • 字符串处理 [string processing]
  • I/O

所有这些工具每天都在被Google的工程师应用在产品服务中。 其中caching这一块是常用的模块的之一

引入依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>27.1-jre</version>
</dependency>

使用示例

基本用法

@Component
public final class WsPlasRoleCache {
	private static Logger logger = LoggerFactory.getLogger(WsPlasRoleCache.class);
	private static AtomicBoolean isInited = new AtomicBoolean(false);
	private static LoadingCache<String, List<WsPlasRole>> cache;

	private WsPlasRoleCache() {
	    if (!isInited.get()) {
            init();
        }
	}

	private static void init() {
		logger.info("---初始化WsPlasRole");
		cache = CacheBuilder.newBuilder().refreshAfterWrite(10, TimeUnit.MINUTES)
				.build(new CacheLoader<String, List<WsPlasRole>>() {
					@Override
					public List<WsPlasRole> load(String key) throws Exception {
					    // 如果是第一次,调用load方法;如果是两次get时间在10分钟之间,就不会调用load
						PlasRoleManager service = SpringContextHolder.getBean("plasRoleManager");
						logger.info("查询WsPlasRole");
						List<WsPlasRole> list = service.getWsAll(key);
						return list;
					}
				});
		isInited.set(true);
	}


	public static List<WsPlasRole> getWsAll(String key) {
		try {
			return cache.get(key);
		} catch (Exception e) {
		}
		return null;
	}
}

利用@Component让工程启动的时候初始化cache对象,重写构造函数并且设置为private是为了保证是单例的

核心方法
  • refreshAfterWrite : 写入数据后多久过期,只阻塞当前数据加载线程,其他线程返回旧值
  • expireAfterWrite : 写缓存后多久过期
  • expireAfterAccess : 读写缓存后多久过期
  • maximumSize : 设置缓存最大容量,超过之后就会按照LRU最近虽少使用算法来移除缓存项

异步刷新

抽象类CacheLoader是同步刷新的。

如果缓存到期了,有多个线程调用缓存,那么第一个调用的会阻塞住,其他的线程会直接返回已过期的缓存

如果想要让所有的线程都不阻塞,就要创建异步加载类

public abstract class RefreshAsyncCacheLoader<K, V> extends CacheLoader<K, V> {
    private ExecutorService threadPool = Executors.newSingleThreadExecutor();
    @Override
    public ListenableFuture<V> reload(final K key, final V oldValue) throws Exception {
        checkNotNull(key);
        checkNotNull(oldValue);
        //增加配置参数,控制是否异步加载;1-异步加载,0-同步加载
        String async = PropertyConfigurer.getProperty("cache.refres.async");
        if (StringUtils.equals(async,"1")){
            ListenableFutureTask<V> task = ListenableFutureTask.create(new Callable<V>() {
                public V call() {
                    try {
                        return load((K) key);
                    } catch (Exception e) {
                    }
                    return oldValue;
                }
            });
            threadPool.execute(task);
            return task;
        }else{
            return Futures.immediateFuture(load(key));
        }

    }

}

将CacheLoader替换成异步实现类RefreshAsyncCacheLoader

public static void init() {
	logger.info("---初始化WsPlasUserCache");
	cache = CacheBuilder.newBuilder().refreshAfterWrite(10, TimeUnit.MINUTES)
			.build(new RefreshAsyncCacheLoader<String, List<WsPlasUser>>() {
				@Override
				public List<WsPlasUser> load(String key) throws Exception {
					PlasUserManager service = SpringContextHolder.getBean("plasUserManager");
					logger.info("查询WsPlasUser");
					List<WsPlasUser> list = service.getWsAll();
					return list;
				}
			});
}

显示清除

缓存会自动过期,也可以手动让其过期,主要有以下方法:

  • 个别清除:Cache.invalidate(key)
  • 批量清除:Cache.invalidateAll(keys)
  • 清除所有缓存项:Cache.invalidateAll()

总结

  • guava cache是本地缓存,和redis、memcached等集中缓存不同,如果是集群环境,本地缓存有不同步的问题,如果对缓存同步时间要求不是太高,这完全不是问题
  • 相比于redis等集中缓存,本地缓存性能更高;因为没有网络交互,直接内存读取
  • guava cache使用简单、控制灵活
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐