• 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方式

      代码如下:

    复制代码
     1 import java.util.ArrayList;
     2 import java.util.List;
     3 import java.util.concurrent.ExecutionException;
     4 import java.util.concurrent.TimeUnit;
     5 
     6 import org.junit.Test;
     7 
     8 import com.google.cacahe.Person;
     9 import com.google.common.cache.CacheBuilder;
    10 import com.google.common.cache.CacheLoader;
    11 import com.google.common.cache.LoadingCache;
    12 
    13 public class TestGuavaCache {
    14 
    15     @Test
    16     public void testUserCacheLoader() throws ExecutionException {
    17         // 模拟数据
    18         final List<Person> list = new ArrayList<Person>(5);
    19         list.add(new Person("1", "zhangsan"));
    20         list.add(new Person("2", "lisi"));
    21         list.add(new Person("3", "wangwu"));
    22 
    23         // 创建cache
    24         LoadingCache<String, Person> cache = CacheBuilder.newBuilder()//
    25                 .refreshAfterWrite(1, TimeUnit.MINUTES)// 给定时间内没有被读/写访问,则回收。
    26                 // .expireAfterWrite(5, TimeUnit.SECONDS)//给定时间内没有写访问,则回收。
    27                 // .expireAfterAccess(3, TimeUnit.SECONDS)// 缓存过期时间为3秒
    28                 .maximumSize(100).// 设置缓存个数
    29                 build(new CacheLoader<String, Person>() {
    30                     @Override
    31                     /**  当本地缓存命没有中时,调用load方法获取结果并将结果缓存
    32                      */
    33                     public Person load(String key) throws ExecutionException {
    34                         System.out.println(key + " load in cache");
    35                         return getPerson(key);
    36                     }
    37 
    38                     // 此时一般我们会进行相关处理,如到数据库去查询
    39                     private Person getPerson(String key) throws ExecutionException {
    40                         System.out.println(key + " query");
    41                         for (Person p : list) {
    42                             if (p.getId().equals(key))
    43                                 return p;
    44                         }
    45                         return null;
    46                     }
    47                 });
    48 
    49         cache.get("1");
    50         cache.get("2");
    51         cache.get("3");
    52         System.out.println("======= sencond time  ==========");
    53         cache.get("1");
    54         cache.get("2");
    55         cache.get("3");
    56     }
    57 }
    复制代码

      执行结果如下:

    复制代码
    1 load in cache
    1 query
    2 load in cache
    2 query
    3 load in cache
    3 query
    ======= sencond time  ==========
    复制代码

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

      2、Callback方式

      代码如下:

    复制代码
     1 import java.util.ArrayList;
     2 import java.util.List;
     3 import java.util.concurrent.Callable;
     4 import java.util.concurrent.ExecutionException;
     5 
     6 import org.junit.Test;
     7 
     8 import com.google.cacahe.Person;
     9 import com.google.common.cache.Cache;
    10 import com.google.common.cache.CacheBuilder;
    11 
    12 public class TestGuavaCache {
    13     
    14 
    15     @Test
    16     public void testUserCallback() throws ExecutionException {
    17         // 模拟数据
    18         final List<Person> list = new ArrayList<Person>(5);
    19         list.add(new Person("1", "zhangsan"));
    20         list.add(new Person("2", "lisi"));
    21         list.add(new Person("3", "wangwu"));
    22 
    23         final String key = "1";
    24         Cache<String, Person> cache2 = CacheBuilder.newBuilder().maximumSize(1000).build();
    25         /**
    26          * 用缓存中的get方法,当缓存命中时直接返回结果;否则,通过给定的Callable类call方法获取结果并将结果缓存。<br/>
    27          * 可以用一个cache对象缓存多种不同的数据,只需创建不同的Callable对象即可。
    28          */
    29         Person person = cache2.get(key, new Callable<Person>() {
    30             public Person call() throws ExecutionException {
    31                 System.out.println(key + " load in cache");
    32                 return getPerson(key);
    33             }
    34 
    35             // 此时一般我们会进行相关处理,如到数据库去查询
    36             private Person getPerson(String key) throws ExecutionException {
    37                 System.out.println(key + " query");
    38                 for (Person p : list) {
    39                     if (p.getId().equals(key))
    40                         return p;
    41                 }
    42                 return null;
    43             }
    44         });
    45         System.out.println("======= sencond time  ==========");
    46         person = cache2.getIfPresent(key);
    47         person = cache2.getIfPresent(key);
    48     }
    49 }
    复制代码

      执行结果如下:

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

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

      3、关于移除监听器

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

      示例代码如下:

    复制代码
     1 import java.util.concurrent.ExecutionException;
     2 import java.util.concurrent.TimeUnit;
     3 
     4 import org.junit.Test;
     5 
     6 import com.google.cacahe.Person;
     7 import com.google.common.cache.CacheBuilder;
     8 import com.google.common.cache.CacheLoader;
     9 import com.google.common.cache.LoadingCache;
    10 import com.google.common.cache.RemovalListener;
    11 import com.google.common.cache.RemovalNotification;
    12 
    13 public class TestGuavaCache {
    14     @Test
    15     public void testListener() throws ExecutionException {
    16         CacheLoader<String, Person> loader = new CacheLoader<String, Person>() {
    17             @Override
    18             // 当本地缓存命没有中时,调用load方法获取结果并将结果缓存
    19             public Person load(String key) throws ExecutionException {
    20                 System.out.println(key + " load in cache");
    21                 return getPerson(key);
    22             }
    23             // 此时一般我们会进行相关处理,如到数据库去查询
    24             private Person getPerson(String key) throws ExecutionException {
    25                 System.out.println(key + " query");
    26                 return new Person(key, "zhang" + key);
    27             }
    28         };
    29 
    30         // remove listener
    31         RemovalListener<String, Person> removalListener = new RemovalListener<String, Person>() {
    32             public void onRemoval(RemovalNotification<String, Person> removal) {
    33                 System.out.println("cause:" + removal.getCause() + " key:" + removal.getKey() + " value:"
    34                         + removal.getValue());
    35             }
    36         };
    37 
    38         LoadingCache<String, Person> cache = CacheBuilder.newBuilder()//
    39                 .expireAfterWrite(2, TimeUnit.MINUTES).maximumSize(1024).removalListener(removalListener).build(loader);
    40         cache.get("1");// 放入缓存
    41         cache.get("1");// 第二次获取(此时从缓存中获取)
    42         cache.invalidate("1");// 移除缓存
    43         cache.get("1");// 重新获取
    44         cache.get("1");// 再次获取(此时从缓存中获取)
    45     }
    46 }
    复制代码

      运行结果如下:

    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就不可用了。

    转载自:https://www.cnblogs.com/always-online/p/4062856.html

  • 相关阅读:
    块拷贝
    Response.AddHeader函数中文件名的中文乱码问题的解决
    c#:如何往List>里添加 Dictionary<string,string>
    java的map中的containsKey 方法——判断是否包含指定的键名
    C# Hashtable 中的ContainsKey()方法
    C#的List的Contains方法 list的Contains方法是根据其元素类型定义的Equals方法来判断是否重复的
    C#从List Dictionary string, string 中查找指定的key,并修改对应的值
    C# 泛型Dictionary<string,string>的用法 ,ContarnsKey() 来判断键是否存存在
    C#去除list中的重复数据 倒叙遍历
    对象引用类型问题
  • 原文地址:https://www.cnblogs.com/PengChengLi/p/10511867.html
Copyright © 2020-2023  润新知