Google Guava之--cache

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 不回收元素,它也会自动加载缓存。

  Guava Cache适用于以下应用场景:

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

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

二、Cache使用方式

  1、CacheLoader方式

  代码如下:

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");

     }

 }


  执行结果如下:

1 load in cache

1 query

2 load in cache

2 query

3 load in cache

3 query

======= sencond time  ==========

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

  2、Callback方式

  代码如下:

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  ==========

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

  3、关于移除监听器

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

  示例代码如下:

 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

 三、其他相关方法

  显式插入:该方法可以直接向缓存中插入值,如果缓存中有相同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就不可用了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值