Google Guava - Cache

1、简介
  Google Guava包含了Google的Java项目许多依赖的库,如:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等。本文只介绍其中的缓存部分。
  Guava Cache是一种本地缓存实现,支持多种缓存过时策略。性能好,简单易用。缓存在不少场景下都是颇有用的。如,经过key获取一个value的花费的时间不少,并且获取的次数不止一次的时候,就应该考虑使用缓存。Guava Cache与ConcurrentMap很类似,但也不彻底同样。最基本的区别是ConcurrentMap会一直保存全部添加的元素,直到显式地移除。而Guava Cache为了限制内存占用,一般都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也会自动加载缓存。java

  Guava Cache适用于如下应用场景:数据库

  • 系统的访问速度首要考虑,而内存空间为次要考虑。
  • 某些key对于的value会被查询屡次。
  • 缓存中存放的数据总量不会超出内存的所有大小。

本文例子使用的guava 版本为guava-18.0.jar,下载地址以下:
http://central.maven.org/maven2/com/google/guava/guava/18.0/guava-18.0.jar缓存

2、Cache使用方式并发

  一、CacheLoader方式maven

  代码以下:ide

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import com.google.cacahe.Person;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

public class TestGuavaCache {

    @Test
    public void testUserCacheLoader() throws ExecutionException {
        // 模拟数据
        final List<Person> list = new ArrayList<Person>(5);
        list.add(new Person("1", "zhangsan"));
        list.add(new Person("2", "lisi"));
        list.add(new Person("3", "wangwu"));

        // 建立cache
        LoadingCache<String, Person> cache = CacheBuilder.newBuilder()//
                .refreshAfterWrite(1, TimeUnit.MINUTES)// 给定时间内没有被读/写访问,则回收。
                // .expireAfterWrite(5, TimeUnit.SECONDS)//给定时间内没有写访问,则回收。
                // .expireAfterAccess(3, TimeUnit.SECONDS)// 缓存过时时间为3秒
                .maximumSize(100).// 设置缓存个数
                build(new CacheLoader<String, Person>() {
                    @Override
                    /**  当本地缓存命没有中时,调用load方法获取结果并将结果缓存
                     */
                    public Person load(String key) throws ExecutionException {
                        System.out.println(key + " load in cache");
                        return getPerson(key);
                    }

                    // 此时通常咱们会进行相关处理,如到数据库去查询
                    private Person getPerson(String key) throws ExecutionException {
                        System.out.println(key + " query");
                        for (Person p : list) {
                            if (p.getId().equals(key))
                                return p;
                        }
                        return null;
                    }
                });

        cache.get("1");
        cache.get("2");
        cache.get("3");
        System.out.println("======= sencond time  ==========");
        cache.get("1");
        cache.get("2");
        cache.get("3");
    }
}

执行结果以下:性能

load in cache
query
load in cache
query
load in cache
query
======= sencond time  ==========

第二次获取的时候没有执行获取的方法,而是直接从缓存中获取。ui

  二、Callback方式google

  代码以下:线程

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

import org.junit.Test;

import com.google.cacahe.Person;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

public class TestGuavaCache {
    

    @Test
    public void testUserCallback() throws ExecutionException {
        // 模拟数据
        final List<Person> list = new ArrayList<Person>(5);
        list.add(new Person("1", "zhangsan"));
        list.add(new Person("2", "lisi"));
        list.add(new Person("3", "wangwu"));

        final String key = "1";
        Cache<String, Person> cache2 = CacheBuilder.newBuilder().maximumSize(1000).build();
        /**
         * 用缓存中的get方法,当缓存命中时直接返回结果;不然,经过给定的Callable类call方法获取结果并将结果缓存。<br/>
         * 能够用一个cache对象缓存多种不一样的数据,只需建立不一样的Callable对象便可。
         */
        Person person = cache2.get(key, new Callable<Person>() {
            public Person call() throws ExecutionException {
                System.out.println(key + " load in cache");
                return getPerson(key);
            }

            // 此时通常咱们会进行相关处理,如到数据库去查询
            private Person getPerson(String key) throws ExecutionException {
                System.out.println(key + " query");
                for (Person p : list) {
                    if (p.getId().equals(key))
                        return p;
                }
                return null;
            }
        });
        System.out.println("======= sencond time  ==========");
        person = cache2.getIfPresent(key);
        person = cache2.getIfPresent(key);
    }
}

执行结果以下:

1 load in cache
1 query
======= sencond time  ==========

第二次获取后也是直接从缓存中加载。

  三、关于移除监听器

  经过CacheBuilder.removalListener(RemovalListener),咱们能够声明一个监听器,从而能够在缓存被移除时作一些其余的操做。当缓存被移除时,RemovalListener会获取移除bing通知[RemovalNotification],其中包含移除的key、value和RemovalCause。

  示例代码以下:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import com.google.cacahe.Person;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

public class TestGuavaCache {
    @Test
    public void testListener() throws ExecutionException {
        CacheLoader<String, Person> loader = new CacheLoader<String, Person>() {
            @Override
            // 当本地缓存命没有中时,调用load方法获取结果并将结果缓存
            public Person load(String key) throws ExecutionException {
                System.out.println(key + " load in cache");
                return getPerson(key);
            }
            // 此时通常咱们会进行相关处理,如到数据库去查询
            private Person getPerson(String key) throws ExecutionException {
                System.out.println(key + " query");
                return new Person(key, "zhang" + key);
            }
        };

        // remove listener
        RemovalListener<String, Person> removalListener = new RemovalListener<String, Person>() {
            public void onRemoval(RemovalNotification<String, Person> removal) {
                System.out.println("cause:" + removal.getCause() + " key:" + removal.getKey() + " value:"
                        + removal.getValue());
            }
        };

        LoadingCache<String, Person> cache = CacheBuilder.newBuilder()//
                .expireAfterWrite(2, TimeUnit.MINUTES).maximumSize(1024).removalListener(removalListener).build(loader);
        cache.get("1");// 放入缓存
        cache.get("1");// 第二次获取(此时从缓存中获取)
        cache.invalidate("1");// 移除缓存
        cache.get("1");// 从新获取
        cache.get("1");// 再次获取(此时从缓存中获取)
    }
}

运行结果以下:

1 1 load in cache
2 1 query
3 cause:EXPLICIT key:1 value:Person [id=1, name=zhang1]
4 1 load in cache
5 1 query

3、其余相关方法

  显式插入:该方法能够直接向缓存中插入值,若是缓存中有相同key则以前的会被覆盖。

cache.put(key, value);

显式清除:咱们也能够对缓存进行手动清除。

cache.invalidate(key); //单个清除
cache.invalidateAll(keys); //批量清除
cache.invalidateAll(); //清除全部缓存项

基于时间的移除: 

expireAfterAccess(long, TimeUnit); 该键值对最后一次访问后超过指定时间再移除
expireAfterWrite(long, TimeUnit) ;该键值对被建立或值被替换后超过指定时间再移除

基于大小的移除:指若是缓存的对象格式即将到达指定的大小,就会将不经常使用的键值对从cache中移除。

cacheBuilder.maximumSize(long)

size是指cache中缓存的对象个数。当缓存的个数开始接近size的时候系统就会进行移除的操做

  缓存清除执行的时间

使用CacheBuilder构建的缓存不会"自动"执行清理和回收工做,也不会在某个缓存项过时后立刻清理,也没有诸如此类的清理机制。它是在写操做时顺带作少许的维护工做(清理);若是写操做太少,读操做的时候也会进行少许维护工做。由于若是要自动地持续清理缓存,就必须有一个线程,这个线程会和用户操做竞争共享锁。在某些环境下线程建立可能受限制,这样CacheBuilder就不可用了。

相关文章
相关标签/搜索