guava缓存

在项目中,如果想把某些数据放到内存缓存中,并且支持自动过期,则可以用guava cache。

要用到的类和接口有CacheBuilder、CacheLoader、Cache、LoadingCache,其中LoadingCache是Cache的子接口。

Cache接口方法有:

V get(K key, Callable<V> loader) throws ExecutionException:从缓存中取指定key对应的值,如果不存在就调用loader加载进缓存,再返回。如果同时有多个线程调用此get()方法,且恰好此时缓存中没有对应的值,则只会有一个线程去加载数据,其他线程会等待,直到缓存中有数据,读取并返回。这样的操作模式,虽然比多个线程都去加载数据好一些,但是依然有些问题。假如并发大的话,会hang住所有线程,导致系统在一定时间内不能提供服务。

示例:

public class Test {
    public static void main(String[] args) throws ExecutionException {
        Cache<String, Object> cache = CacheBuilder.newBuilder().build();
        Object object = cache.get("first", Test::getFirst);
        System.out.println(object);
        object = cache.get("first", Test::getFirst);
        System.out.println(object);
    }

    private static List<String> getFirst() {
        System.out.println("getFirst");
        return Lists.newArrayList(UUID.randomUUID().toString(), UUID.randomUUID().toString());
    }

}

V getIfPresent(Object key):从缓存中取指定key对应的值,如果不存在就返回null,而不会去加载数据。

ImmutableMap getAllPresent(Iterable keys):从缓存中取多个key对应的值。如果某个key对应的值不存在,则返回的Map不包含此键。极端情况下,返回一个空Map。

void put(K, V):把一个键值对放到缓存中。不推荐用这个方法,推荐优先使用get(K key, Callable<V> loader)方法。

void putAll(Map<K, V> map):把一堆键值对放到缓存中。

void invalidate(Object key):从缓存中移除某个key对应的值。

void invalidateAll(Iterable keys):从缓存中移除多个key对应的值。

void invalidateAll():从缓存中移除所有key对应的值,也就是清空缓存。

long size():返回缓存中key的个数。是个大概数,不完全精确。

CacheStats stats():返回一个CacheStats实例,通过这个实例可以得到命中缓存次数、未命中缓存的次数等等,前提是创建Cache时调用了CacheBuilder的recordStats()方法。我们可以用这个做缓存的监控。

示例:

    public static void main(String[] args) throws ExecutionException {
        Cache<String, String> cc = CacheBuilder.newBuilder().recordStats().build();
        cc.get("a", () -> UUID.randomUUID().toString());
        cc.get("b", () -> UUID.randomUUID().toString());
        ImmutableMap ss = cc.getAllPresent(Sets.newHashSet("a", "b", "c", "d"));
        System.out.println(ss);
    }

注意,Cache没有get(K key)方法,用getIfPresent(K key)就行。

LoadingCache接口继承了Cache接口,除了继承自Cache接口的全部方法外,自己定义的方法有:

V get(K key) throws ExecutionException:从缓存中获取key对应的值,如果不存在,就调用CacheLoader实例的方法加载数据进缓存。同样的,如果有多个线程同时get同一个key,也只会有一个线程去加载,其他线程会阻塞,直到缓存中有数据,读取并返回。如果有多个线程同时get多个key,则每个key都会有一个线程去加载对应数据,其他多余线程阻塞直至对应key有数据,读取并返回。

V getUnchecked(K key):效果同get(K key)方法,只是不抛ExecutionException异常。

ImmutableMap<K, V> getAll(Iterable<K> keys) throws ExecutionException:从缓存中获取多个key对应的值,哪个key对应的数据不存在,会去加载这个key对应的数据。

void refresh(K key):重新加载key对应的数据进缓存。refresh会根据当前缓存中有没有对应的数据执行不同的逻辑:如果当前缓存中有对应的数据,则会调用CacheLoader实例的reload()方法。否则会调用CacheLoader实例的load()方法。在refresh期间,如果有读请求,则会返回老值。如果没有老值,则会阻塞,等待refresh完成。如果reload()或者load()是异步的,则可能会返回null。

ConcurrentMap asMap():查看缓存中数据的视图。

示例:

    LoadingCache<Integer, Map<Integer, List<Long>>> allChannelPhotoCache = CacheBuilder.newBuilder()
            .concurrencyLevel(8)
            .initialCapacity(8)
            .maximumSize(100)
            .build(new CacheLoader<Integer, Map<Integer, List<Long>>>() {
                       @Override
                       public Map<Integer, List<Long>> load(Integer key) {
                           monitorLogger.info("currentThread= " + Thread.currentThread().getName() + ", load cache key= " + key);
                           if (1 == key) {
                               return getChannelPhotoIds();
                           } else {
                               return new HashMap<>(0);
                           }
                       }
                   }
            );

通过CacheBuilder.newBuilder().build(CacheLoader loader)创建一个Cache实例,具体类型是LocalCache类中的LocalLoadingCache内部类。

CacheBuilder.newBuilder()生成一个CacheBuilder实例。CacheBuilder有很多实例方法:

CacheBuilder concurrencyLevel(int concurrencyLevel):设置更新缓存的并发度,默认是4。会根据并发度的值把哈希表分段,每个段都有一个分段锁,用于控制对这一段数据的更新。

CacheBuilder initialCapacity(int initialCapacity):设置哈希表的初始容量,默认值是16。

CacheBuilder maximumSize(int maximumSize):设置哈希表最多能放的键值对数量。

原文地址:https://www.cnblogs.com/koushr/p/11942415.html