• Guava Cache的使用方式


    maven依赖

    1. <dependency>
    2. <groupId>com.google.guavagroupId>
    3. <artifactId>guavaartifactId>
    4. <version>28.2-jreversion>
    5. dependency>

    1、Guava Cache创建方式

    GuavaCache有两种创建方式:CacheLoaderCallable callback

    1. package com.lg.guava.demo.guava;
    2. import com.google.common.cache.CacheBuilder;
    3. import com.google.common.cache.CacheLoader;
    4. import com.google.common.cache.LoadingCache;
    5. import com.lg.guava.demo.Constants;
    6. import java.util.Iterator;
    7. import java.util.concurrent.Callable;
    8. import java.util.concurrent.ExecutionException;
    9. public class Demo1 {
    10. /**
    11. * 初始化缓存三个
    12. */
    13. public static void initCache(LoadingCache cache) throws Exception {
    14. for(int i=1;i<=3;i++){
    15. //连接数据源 如果缓存没有则读取数据源
    16. cache.get(String.valueOf(i));
    17. }
    18. }
    19. /**
    20. * 显示缓存里的数据
    21. * @param cache
    22. */
    23. public static void display(LoadingCache cache){
    24. //利用迭代器
    25. Iterator its=cache.asMap().entrySet().iterator();
    26. while(its.hasNext()){
    27. System.out.println(its.next().toString());
    28. }
    29. }
    30. /**
    31. * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
    32. * @param key
    33. * @param cache
    34. */
    35. public static void get(String key,LoadingCache cache) throws Exception {
    36. cache.get(key, new Callable() {
    37. @Override //回调方法用于读源并写入缓存
    38. public Object call() throws Exception {
    39. //读源
    40. Object value=Constants.hm.get(key);
    41. //写回缓存
    42. // cache.put(key,value);
    43. return value;
    44. }
    45. });
    46. }
    47. public static void main(String[] args) throws Exception {
    48. //CacheLoader的方式创建
    49. LoadingCache cache= CacheBuilder.newBuilder().build(new CacheLoader() {
    50. //读取数据源
    51. @Override
    52. public Object load(String key) throws Exception {
    53. return Constants.hm.get(key);
    54. }
    55. });
    56. //初始化缓存
    57. initCache(cache);
    58. System.out.println(cache.size());
    59. //显示缓存数据
    60. display(cache);
    61. //读取4
    62. get("4",cache);
    63. System.out.println("==================================");
    64. display(cache);
    65. System.out.println("==================================");
    66. display(cache);
    67. }
    68. }
    69. 2、缓存数据删除

      GuavaCache的数据删除分为:被动删除主动删除

      2.1、被动删除

      (1)基于数据大小的删除

      1. LoadingCache cache= CacheBuilder.newBuilder()
      2. /*
      3. 加附加的功能
      4. */
      5. //最大个数
      6. .maximumSize(3)
      7. .build(new CacheLoader() {
      8. //读取数据源
      9. @Override
      10. public Object load(String key) throws Exception {
      11. return Constants.hm.get(key);
      12. }
      13. });
      14. //读取缓存中的1的数据 缓存有就读取 没有就返回null
      15. System.out.println(cache.getIfPresent("5"));
      16. //读取4 读源并回写缓存 淘汰一个(LRU+FIFO)
      17. get("4",cache);
      • 规则:LRU+FIFO
      • 访问次数一样少的情况下,FIFO
      1. package com.lg.guava.demo.guava;
      2. import com.google.common.cache.*;
      3. import com.lg.guava.demo.Constants;
      4. import java.util.Iterator;
      5. import java.util.concurrent.Callable;
      6. import java.util.concurrent.TimeUnit;
      7. public class Demo2 {
      8. /**
      9. * 初始化缓存三个
      10. */
      11. public static void initCache(LoadingCache cache) throws Exception {
      12. for (int i = 1; i <= 3; i++) {
      13. //连接数据源 如果缓存没有则读取数据源
      14. cache.get(String.valueOf(i));
      15. }
      16. }
      17. /**
      18. * 显示缓存里的数据
      19. *
      20. * @param cache
      21. */
      22. public static void display(LoadingCache cache) {
      23. //利用迭代器
      24. Iterator its = cache.asMap().entrySet().iterator();
      25. while (its.hasNext()) {
      26. System.out.println(its.next().toString());
      27. }
      28. }
      29. /**
      30. * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
      31. *
      32. * @param key
      33. * @param cache
      34. */
      35. public static void get(String key, LoadingCache cache) throws Exception {
      36. cache.get(key, new Callable() {
      37. @Override //回调方法用于读源并写入缓存
      38. public Object call() throws Exception {
      39. //读源
      40. Object value = Constants.hm.get(key);
      41. //写回缓存
      42. // cache.put(key,value);
      43. return value;
      44. }
      45. });
      46. }
      47. public static void main(String[] args) throws Exception {
      48. //CacheLoader的方式创建
      49. LoadingCache cache = CacheBuilder.newBuilder()
      50. /*
      51. 加附加的功能
      52. */
      53. //最大个数
      54. .maximumSize(3)
      55. .concurrencyLevel(Runtime.getRuntime().availableProcessors())
      56. .refreshAfterWrite(1, TimeUnit.SECONDS)
      57. //统计命中率
      58. .recordStats()
      59. //移除通知
      60. .removalListener(new RemovalListener() {
      61. @Override
      62. public void onRemoval(RemovalNotification removalNotification) {
      63. //移除的key 移除的原因
      64. System.out.println(removalNotification.getKey() + ":" + removalNotification.getCause());
      65. }
      66. })
      67. .build(new CacheLoader() {
      68. //读取数据源
      69. @Override
      70. public Object load(String key) throws Exception {
      71. return Constants.hm.get(key);
      72. }
      73. });
      74. //初始化缓存
      75. initCache(cache);
      76. System.out.println(cache.size());
      77. //显示缓存数据
      78. display(cache);
      79. //读取缓存中的1的数据 缓存有就读取 没有就返回null
      80. System.out.println(cache.getIfPresent("1"));
      81. //读取4 读源并回写缓存 淘汰一个(LRU+FIFO)
      82. get("4", cache);
      83. System.out.println("==================================");
      84. display(cache);
      85. //打印输出统计
      86. System.out.println(cache.stats().toString()+", " + cache.stats().hitRate());
      87. }
      88. }
      89. (2)基于过期时间的删除

        隔多长时间后没有被访问过的key被删除

        .maximumSize(3).expireAfterAccess(3, TimeUnit.SECONDS)

        写入多长时间后过期

        //等同于expire ttl 缓存中对象的生命周期就是3秒
        .maximumSize(3).expireAfterWrite(3, TimeUnit.SECONDS)

        1. package com.lg.guava.demo.guava;
        2. import com.google.common.cache.*;
        3. import com.lg.guava.demo.Constants;
        4. import java.util.Iterator;
        5. import java.util.concurrent.Callable;
        6. import java.util.concurrent.TimeUnit;
        7. public class Demo2 {
        8. /**
        9. * 初始化缓存三个
        10. */
        11. public static void initCache(LoadingCache cache) throws Exception {
        12. for (int i = 1; i <= 3; i++) {
        13. //连接数据源 如果缓存没有则读取数据源
        14. cache.get(String.valueOf(i));
        15. }
        16. }
        17. /**
        18. * 显示缓存里的数据
        19. *
        20. * @param cache
        21. */
        22. public static void display(LoadingCache cache) {
        23. //利用迭代器
        24. Iterator its = cache.asMap().entrySet().iterator();
        25. while (its.hasNext()) {
        26. System.out.println(its.next().toString());
        27. }
        28. }
        29. /**
        30. * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
        31. *
        32. * @param key
        33. * @param cache
        34. */
        35. public static void get(String key, LoadingCache cache) throws Exception {
        36. cache.get(key, new Callable() {
        37. @Override //回调方法用于读源并写入缓存
        38. public Object call() throws Exception {
        39. //读源
        40. Object value = Constants.hm.get(key);
        41. //写回缓存
        42. // cache.put(key,value);
        43. return value;
        44. }
        45. });
        46. }
        47. public static void main(String[] args) throws Exception {
        48. //CacheLoader的方式创建
        49. LoadingCache cache = CacheBuilder.newBuilder()
        50. /*
        51. 加附加的功能
        52. */
        53. //最大个数
        54. .maximumSize(3)
        55. .concurrencyLevel(Runtime.getRuntime().availableProcessors())
        56. .refreshAfterWrite(1, TimeUnit.SECONDS)
        57. //统计命中率
        58. .recordStats()
        59. //移除通知
        60. .removalListener(new RemovalListener() {
        61. @Override
        62. public void onRemoval(RemovalNotification removalNotification) {
        63. //移除的key 移除的原因
        64. System.out.println(removalNotification.getKey() + ":" + removalNotification.getCause());
        65. }
        66. })
        67. .build(new CacheLoader() {
        68. //读取数据源
        69. @Override
        70. public Object load(String key) throws Exception {
        71. return Constants.hm.get(key);
        72. }
        73. });
        74. //初始化缓存
        75. initCache(cache);
        76. System.out.println(cache.size());
        77. //显示缓存数据
        78. display(cache);
        79. //读取缓存中的1的数据 缓存有就读取 没有就返回null
        80. System.out.println(cache.getIfPresent("1"));
        81. //读取4 读源并回写缓存 淘汰一个(LRU+FIFO)
        82. get("4", cache);
        83. System.out.println("==================================");
        84. display(cache);
        85. //打印输出统计
        86. System.out.println(cache.stats().toString()+", " + cache.stats().hitRate());
        87. }
        88. }
          1. package com.lg.guava.demo.guava;
          2. import com.google.common.cache.CacheBuilder;
          3. import com.google.common.cache.CacheLoader;
          4. import com.google.common.cache.LoadingCache;
          5. import com.lg.guava.demo.Constants;
          6. import java.util.Iterator;
          7. import java.util.concurrent.Callable;
          8. import java.util.concurrent.TimeUnit;
          9. public class Demo3 {
          10. /**
          11. * 初始化缓存三个
          12. */
          13. public static void initCache(LoadingCache cache) throws Exception {
          14. for(int i=1;i<=3;i++){
          15. //连接数据源 如果缓存没有则读取数据源
          16. cache.get(String.valueOf(i));
          17. }
          18. }
          19. /**
          20. * 显示缓存里的数据
          21. * @param cache
          22. */
          23. public static void display(LoadingCache cache){
          24. //利用迭代器
          25. Iterator its=cache.asMap().entrySet().iterator();
          26. while(its.hasNext()){
          27. System.out.println(its.next().toString());
          28. }
          29. }
          30. /**
          31. * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
          32. * @param key
          33. * @param cache
          34. */
          35. public static void get(String key,LoadingCache cache) throws Exception {
          36. cache.get(key, new Callable() {
          37. @Override //回调方法用于读源并写入缓存
          38. public Object call() throws Exception {
          39. //读源
          40. Object value=Constants.hm.get(key);
          41. //写回缓存
          42. // cache.put(key,value);
          43. return value;
          44. }
          45. });
          46. }
          47. public static void main(String[] args) throws Exception {
          48. //CacheLoader的方式创建
          49. LoadingCache cache= CacheBuilder.newBuilder()
          50. /*
          51. 加附加的功能
          52. */
          53. //等同于expire ttl 缓存中对象的生命周期就是3秒
          54. .maximumSize(3).expireAfterWrite(3, TimeUnit.SECONDS)
          55. .build(new CacheLoader() {
          56. //读取数据源
          57. @Override
          58. public Object load(String key) throws Exception {
          59. return Constants.hm.get(key);
          60. }
          61. });
          62. //初始化缓存
          63. initCache(cache);
          64. System.out.println(cache.size());
          65. //显示缓存数据
          66. display(cache);
          67. Thread.sleep(1000);
          68. //访问1
          69. cache.getIfPresent("1");
          70. //歇了2.1秒
          71. Thread.sleep(2100);
          72. System.out.println("==================================");
          73. display(cache);
          74. }
          75. }
          76. (3)基于引用的删除

                    可以通过weakKeys和weakValues方法指定Cache只保存对缓存记录key和value的弱引用。这样当没有其他强引用指向key和value时,key和value对象就会被垃圾回收器回收。

            LoadingCache cache = CacheBuilder.newBuilder()
                    // 最大3个 值的弱引用
                    .maximumSize(3).
            weakValues()
                    .build(
            );

            1. package com.lg.guava.demo.guava;
            2. import com.google.common.cache.CacheBuilder;
            3. import com.google.common.cache.CacheLoader;
            4. import com.google.common.cache.LoadingCache;
            5. import com.lg.guava.demo.Constants;
            6. import java.util.Iterator;
            7. import java.util.concurrent.Callable;
            8. import java.util.concurrent.TimeUnit;
            9. public class Demo4 {
            10. /**
            11. * 初始化缓存三个
            12. */
            13. public static void initCache(LoadingCache cache) throws Exception {
            14. for (int i = 1; i <= 3; i++) {
            15. //连接数据源 如果缓存没有则读取数据源
            16. cache.get(String.valueOf(i));
            17. }
            18. }
            19. /**
            20. * 显示缓存里的数据
            21. *
            22. * @param cache
            23. */
            24. public static void display(LoadingCache cache) {
            25. //利用迭代器
            26. Iterator its = cache.asMap().entrySet().iterator();
            27. while (its.hasNext()) {
            28. System.out.println(its.next().toString());
            29. }
            30. }
            31. /**
            32. * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
            33. *
            34. * @param key
            35. * @param cache
            36. */
            37. public static void get(String key, LoadingCache cache) throws Exception {
            38. cache.get(key, new Callable() {
            39. @Override //回调方法用于读源并写入缓存
            40. public Object call() throws Exception {
            41. //读源
            42. Object value = Constants.hm.get(key);
            43. //写回缓存
            44. // cache.put(key,value);
            45. return value;
            46. }
            47. });
            48. }
            49. public static void main(String[] args) throws Exception {
            50. //CacheLoader的方式创建
            51. LoadingCache cache = CacheBuilder.newBuilder()
            52. /*
            53. 加附加的功能
            54. */
            55. //弱值的删除
            56. .maximumSize(3).weakValues()
            57. .build(new CacheLoader() {
            58. //读取数据源
            59. @Override
            60. public Object load(String key) throws Exception {
            61. return Constants.hm.get(key);
            62. }
            63. });
            64. //初始化缓存
            65. initCache(cache);
            66. System.out.println(cache.size());
            67. //显示缓存数据
            68. display(cache);
            69. Object v = new Object();
            70. cache.put("1", v);
            71. v = new Object(); //原对象不再有强引用
            72. //强制垃圾回收
            73. System.gc();
            74. System.out.println("================================");
            75. display(cache);
            76. }
            77. }
            78. 2.2、主动删除

              (1)单独删除

              // 将key=1 删除
              cache.invalidate("1");

              (2)批量删除

              // 将key=1和2的删除
              cache.invalidateAll(Arrays.asList("1","2"));

              (3)清空所有数据

              // 清空缓存
              cache.invalidateAll();

              1. package com.lg.guava.demo.guava;
              2. import com.google.common.cache.CacheBuilder;
              3. import com.google.common.cache.CacheLoader;
              4. import com.google.common.cache.LoadingCache;
              5. import com.lg.guava.demo.Constants;
              6. import java.util.Arrays;
              7. import java.util.Iterator;
              8. import java.util.concurrent.Callable;
              9. /**
              10. * 主动删除
              11. */
              12. public class Demo5 {
              13. /**
              14. * 初始化缓存三个
              15. */
              16. public static void initCache(LoadingCache cache) throws Exception {
              17. for (int i = 1; i <= 3; i++) {
              18. //连接数据源 如果缓存没有则读取数据源
              19. cache.get(String.valueOf(i));
              20. }
              21. }
              22. /**
              23. * 显示缓存里的数据
              24. *
              25. * @param cache
              26. */
              27. public static void display(LoadingCache cache) {
              28. //利用迭代器
              29. Iterator its = cache.asMap().entrySet().iterator();
              30. while (its.hasNext()) {
              31. System.out.println(its.next().toString());
              32. }
              33. }
              34. /**
              35. * 读取缓存数据 如果没有则回调源数据并(自动)写入缓存
              36. *
              37. * @param key
              38. * @param cache
              39. */
              40. public static void get(String key, LoadingCache cache) throws Exception {
              41. cache.get(key, new Callable() {
              42. @Override //回调方法用于读源并写入缓存
              43. public Object call() throws Exception {
              44. //读源
              45. Object value = Constants.hm.get(key);
              46. //写回缓存
              47. // cache.put(key,value);
              48. return value;
              49. }
              50. });
              51. }
              52. public static void main(String[] args) throws Exception {
              53. //CacheLoader的方式创建
              54. LoadingCache cache = CacheBuilder.newBuilder()
              55. /*
              56. 加附加的功能
              57. */
              58. //弱值的删除
              59. .maximumSize(3).weakValues()
              60. .build(new CacheLoader() {
              61. //读取数据源
              62. @Override
              63. public Object load(String key) throws Exception {
              64. return Constants.hm.get(key);
              65. }
              66. });
              67. //初始化缓存
              68. initCache(cache);
              69. System.out.println(cache.size());
              70. //显示缓存数据
              71. display(cache);
              72. System.out.println("================================");
              73. //清空
              74. // cache.invalidateAll();
              75. //删除指定key
              76. // cache.invalidate("1");
              77. //删多个
              78. cache.invalidateAll(Arrays.asList("1","3"));
              79. display(cache);
              80. }
              81. }
              82. 3、Guava Cache原理

                3.1、GuavaCache核心原理之数据结构

                • Guava Cache的数据结构跟ConcurrentHashMap类似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。
                • 相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。其数据结构图如下:

                • LocalCache为Guava Cache的核心类,包含一个Segment数组组成
                • Segement数组的长度决定了cache的并发数
                • 每一个Segment使用了单独的锁,其实每个Segment继承了ReentrantLock,对Segment的写操作需要先拿到锁
                • 每个Segment由一个table和5个队列组成 
                • 5个队列:
                  • ReferenceQueue keyReferenceQueue : 已经被GC,需要内部清理的键引用队列
                  • ReferenceQueue valueReferenceQueue : 已经被GC,需要内部清理的值引用队列
                  • ConcurrentlinkedQueue> recencyQueue : LRU队列,当segment上达到临界值发生写操作时该队列会移除数据
                  • Queue> writeQueue:写队列,按照写入时间进行排序的元素队列,写入一个元素时会把它加入到队列尾部
                  • Queue> accessQueue:访问队列,按照访问时间进行排序的元素队列,访问(包括写入)一个元素时会把它加入到队列尾部
                • 1个table:

                       AtomicReferenceArray> table:AtomicReferenceArray可以用原子方式更新其元素的对象引用数组

                • ReferenceEntry
                  • ReferenceEntry是Guava Cache中对一个键值对节点的抽象,每个ReferenceEntry数组项都是一条ReferenceEntry链。并且一个ReferenceEntry包含key、hash、valueReference、next字段(单链)
                  • Guava Cache使用ReferenceEntry接口来封装一个键值对,而用ValueReference来封装Value值

                3.2、GuavaCache核心原理之回收机制

                Guava Cache提供了三种基本的缓存回收方式:

                基于容量回收:在缓存项的数目达到限定值之前,采用LRU的回收方式

                定时回收:

                • expireAfterAccess:缓存项在给定时间内没有被读/写访问,则回收。回收顺序和基于大小回收一样(LRU)
                • expireAfterWrite:缓存项在给定时间内没有被写访问(创建或覆盖),则回收

                基于引用回收:通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以垃圾回收

                除了以上三种还有主动删除,采用命令,这个前面讲了

                • GuavaCache构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。
                • GuavaCache是在每次进行缓存操作的时候,惰性删除 如get()或者put()的时候,判断缓存是否过期

                3.3、GuavaCache核心原理之Segment定位

                • 先通过key做hash定位到所在的Segment
                • 通过位运算找首地址的偏移量 SegmentCount>=并发数且为2的n次方
                1. V get(K key, CacheLoadersuper K, V> loader) throws ExecutionException {
                2. // 注意,key不可为空
                3. int hash = hash(checkNotNull(key));
                4. // 通过hash定位到segment数组的某个Segment元素,然后调用其get方法
                5. return segmentFor(hash).get(key, hash, loader);
                6. }

                再找到segment中的Entry链数组,通过key的hash定位到某个Entry节点

                1. V get(K key, int hash, CacheLoadersuper K, V> loader) throws ExecutionException {
                2. checkNotNull(key);
                3. checkNotNull(loader);
                4. try {
                5. if (count != 0) { // read-volatile
                6. // 内部也是通过找Entry链数组定位到某个Entry节点
                7. ReferenceEntry e = getEntry(key, hash);
                8. ......

                4、Guava Cache高级实战

                4.1、GuavaCache高级实战之并发操作

                (1)并发设置

                GuavaCache通过设置 concurrencyLevel 使得缓存支持并发的写入和读取

                1. LoadingCache cache = CacheBuilder.newBuilder()
                2. // 最大3个 同时支持CPU核数线程写缓存
                3. .maximumSize(3).concurrencyLevel(Runtime.getRuntime().availableProcessors()).build();

                concurrencyLevel=Segment数组的长度

                同ConcurrentHashMap类似Guava cache的并发也是通过分离锁实现

                1. V get(K key, CacheLoadersuper K, V> loader) throws ExecutionException {
                2. int hash = this.hash(Preconditions.checkNotNull(key));
                3. //通过hash值确定该key位于哪一个segment上,并获取该segment
                4. return this.segmentFor(hash).get(key, hash, loader);
                5. }

                        LoadingCache采用了类似ConcurrentHashMap的方式,将映射表分为多个segment。segment之间可以并发访问,这样可以大大提高并发的效率,使得并发冲突的可能性降低了。

                (2)更新锁定

                • GuavaCache提供了一个refreshAfterWrite定时刷新数据的配置项
                • 如果经过一定时间没有更新或覆盖,则会在下一次获取该值的时候,会在后台异步去刷新缓存
                • 刷新时只有一个请求回源取数据,其他请求会阻塞(block)在一个固定时间段,如果在该时间段内没有获得新值则返回旧值。
                1. LoadingCache cache = CacheBuilder.newBuilder()
                2. // 最大3个 同时支持CPU核数线程写缓存
                3. .maximumSize(3).concurrencyLevel(Runtime.getRuntime().availableProcessors())
                4. //3秒内阻塞会返回旧数据
                5. .refreshAfterWrite(3,TimeUnit.SECONDS).build();

                4.2、GuavaCache高级实战之动态加载

                        动态加载行为发生在获取不到数据或者是数据已经过期的时间点,Guava动态加载使用回调模式用户自定义加载方式,然后Guava cache在需要加载新数据时会回调用户的自定义加载方式

                segmentFor(hash).get(key, hash, loader)

                loader即为用户自定义的数据加载方式,当某一线程get不到数据会去回调该自定义加载方式去加载数据

                4.3、GuavaCache高级实战之自定义LRU算法

                1. package com.lg.guava.demo.guava.lru;
                2. import java.util.LinkedHashMap;
                3. import java.util.Map;
                4. public class LRUcache extends LinkedHashMap {
                5. private final int limit;
                6. public LRUcache(int limit) {
                7. //初始化 accessOrder : true 改变尾结点
                8. super(16, 0.75f, true);
                9. this.limit = limit;
                10. }
                11. //是否删除最老的数据
                12. @Override
                13. protected boolean removeEldestEntry(Map.Entry eldest) {
                14. return size() > limit;
                15. }
                16. }
                1. package com.lg.guava.demo.guava.lru;
                2. public class LinkedHashLRUcache {
                3. /**
                4. * LinkedHashMap(自身实现了LRU算法)
                5. * 有序
                6. * 每次访问一个元素,都会加到尾部
                7. */
                8. int limit;
                9. LRUcache lruCache;
                10. public LinkedHashLRUcache(int limit) {
                11. this.limit = limit;
                12. this.lruCache = new LRUcache(limit);
                13. }
                14. public void put(k key, v value) {
                15. this.lruCache.put(key, value);
                16. }
                17. public v get(k key) {
                18. return this.lruCache.get(key);
                19. }
                20. public static void main(String[] args) {
                21. LinkedHashLRUcache lru = new LinkedHashLRUcache(3);
                22. lru.put(1, "zhangfei1");
                23. lru.put(2, "zhangfei2");
                24. lru.put(3, "zhangfei3");
                25. lru.get(1);
                26. lru.put(4, "zhangfei4");
                27. for (Object o : lru.lruCache.values()) {
                28. System.out.println(o.toString());
                29. }
                30. }
                31. }

                4.4、GuavaCache高级实战之疑难问题

                (1)GuavaCache会oom(内存溢出)吗

                会,当我们设置缓存永不过期(或者很长),缓存的对象不限个数(或者很大)时,比如:

                1. Cache cache = CacheBuilder.newBuilder()
                2. .expireAfterWrite(100000, TimeUnit.SECONDS)
                3. .build();

                不断向GuavaCache加入大字符串,最终将会oom

                解决方案:缓存时间设置相对小些,使用弱引用方式存储对象

                1. Cache cache = CacheBuilder.newBuilder()
                2. .expireAfterWrite(1, TimeUnit.SECONDS)
                3. .weakValues().build();

                (2)GuavaCache缓存到期就会立即清除吗

                不是的,GuavaCache是在每次进行缓存操作的时候,如get()或者put()的时候,判断缓存是否过期

                1. void evictEntries(ReferenceEntry e) {
                2. drainRecencyQueue();
                3. while ((e = writeQueue.peek()) != null && map.isExpired(e, now)) {
                4. if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
                5. throw new AssertionError();
                6. }
                7. }
                8. while ((e = accessQueue.peek()) != null && map.isExpired(e, now)) {
                9. if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
                10. throw new AssertionError();
                11. }
                12. }
                13. }

                        一个如果一个对象放入缓存以后,不在有任何缓存操作(包括对缓存其他key的操作),那么该缓存不会主动过期的。

                (3)GuavaCache如何找出最久未使用的数据

                • 用accessQueue,这个队列是按照LRU的顺序存放的缓存对象(ReferenceEntry)的。会把访问过的对象放到队列的最后。
                • 并且可以很方便的更新和删除链表中的节点,因为每次访问的时候都可能需要更新该链表,放入到链表的尾部。
                • 这样,每次从access中拿出的头节点就是最久未使用的。
                • 对应的writeQueue用来保存最久未更新的缓存队列,实现方式和accessQueue一样。

                5、Guava Cache源码剖析

                GuavaCache源码剖析之实现框架

                GuavaCache体系类图:

                • CacheBuilder:类,缓存构建器。构建缓存的入口,指定缓存配置参数并初始化本地缓存。CacheBuilder在build方法中,会把前面设置的参数,全部传递给LocalCache,它自己实际不参与任何计算

                  (1)缓存构建器。构建缓存的入口,指定缓存配置参数并初始化本地缓存。
                  (2)主要采用builder的模式,CacheBuilder的每一个方法都返回这个CacheBuilder知道build方法的调用。注意build方法有重载,带有参数的为构建一个具有数据加载功能的缓存,不带参数的构建一个没有数据加载功能的缓存。

                • CacheLoader:抽象类。用于从数据源加载数据,定义load、reload、loadAll等操作
                • Cache:接口,定义get、put、invalidate等操作,这里只有缓存增删改的操作,没有数据加载的操作
                • LoadingCache:接口,继承自Cache。定义get、getUnchecked、getAll等操作,这些操作都会从数据源load数据
                • LocalCache:类。整个guava cache的核心类,包含了guava cache的数据结构以及基本的缓存的操作方法

                  (1)LoadingCache这些类表示获取Cache的方式,可以有多种方式,但是它们的方法最终调用到LocalCache的方法,LocalCache是Guava Cache的核心类。

                  (2)LocalCache为Guava Cache的核心类 LocalCache的数据结构与ConcurrentHashMap很相似,都由多个segment组成,且各segment相对独立,互不影响,所以能支持并行操作。

                  (3)每个segment由一个table和若干队列组成。缓存数据存储在table中,其类型为AtomicReferenceArray。

                • LocalManualCache:LocalCache内部静态类,实现Cache接口。其内部的增删改缓存操作全部调用成员变量localCache(LocalCache类型)的相应方法
                • LocalLoadingCache:LocalCache内部静态类,继承自LocalManualCache类,实现LoadingCache接口。其所有操作也是调用成员变量localCache(LocalCache类型)的相应方法 
              83. 相关阅读:
                新白娘子传奇系列
                阿里云服务器如何购买?三种方式可买(图文教程举例)
                YOLOv5使用方法记录
                JAVAEE 初阶 多线程基础(二)
                谷歌浏览器自带的翻译插件为什么不能用
                手写RPC框架 第六天 负载均衡
                canal server 标准化集群搭建(一)
                C语言-手写Map(全功能)
                C语言经典算法实例4:判断回文数
                【Linux】进程间通信 -- 共享内存
              84. 原文地址:https://blog.csdn.net/weixin_52851967/article/details/127796512