• Guava缓存及Guava线程池


    Guava缓存

    Guava缓存是Google Guava库中提供的一种缓存实现,可以帮助我们在应用程序中轻松实现缓存功能。Guava缓存提供了一些高级功能,例如自动加载、过期时间、最大缓存大小、缓存回收等。


    CacheBuilder跟Guava缓存的关系

    CacheBuilder是Guava缓存的构建器,提供了一些方法来配置缓存的行为,例如设置缓存的最大大小、过期时间、缓存回收策略等。通过CacheBuilder,可以创建一个Guava缓存实例。
    即CacheBuilder是Guava缓存的一部分,提供了一种方便的方式来创建和配置Guava缓存实例。


    CacheBuilder类的常用方法

    maximumSize(int size)

    作用:设置缓存的最大容量,当超过这个容量的时候,最近最少使用的条目会被回收。

    
    Cache<String, String> cache = CacheBuilder.newBuilder()
            .maximumSize(100)
            .build();
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    maximumWeight(long weight)

    作用:设置缓存的最大重量,当超过这个重量的时候,最近最少使用的条目会被回收。

    
    Cache<String, String> cache = CacheBuilder.newBuilder()
            .maximumWeight(1000)
            .build();
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    expireAfterAccess(long duration, TimeUnit unit)

    作用:在指定时间内没有被读/写访问,则回收缓存条目。

    
    Cache<String, String> cache = CacheBuilder.newBuilder()
            .expireAfterAccess(10, TimeUnit.MINUTES)
            .build();
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    expireAfterWrite(long duration, TimeUnit unit)

    作用:在指定时间内没有被写访问,则回收缓存条目。

    
    Cache<String, String> cache = CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build();
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    refreshAfterWrite(long duration, TimeUnit unit)
    作用:设置缓存项在写入一定时间后自动刷新。

    
    Cache<String, String> cache = CacheBuilder.newBuilder()
            .refreshAfterWrite(10, TimeUnit.MINUTES)
            .build();
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    initialCapacity(int capacity)

    作用:设置缓存的初始容量,用于控制缓存的内存分配。

    
    Cache<String, String> cache = CacheBuilder.newBuilder()
            .initialCapacity(100)
            .build();
    
    • 1
    • 2
    • 3
    • 4

    recordStats()

    作用:启用缓存统计信息,可以获取缓存的命中率、平均加载时间等指标。

    
    Cache<String, String> cache = CacheBuilder.newBuilder()
            .recordStats()
            .build();
    
    • 1
    • 2
    • 3
    • 4

    build()

    作用:构建缓存实例。

    
    Cache<String, String> cache = CacheBuilder.newBuilder()
            .maximumSize(100)
            .build();
    
    • 1
    • 2
    • 3
    • 4

    build方法用于构建一个缓存对象。接受一个CacheLoader对象作为参数,用于定义缓存的加载逻辑。CacheLoader是一个抽象类,需要实现load方法来定义如何加载缓存数据。

    build方法定义:

    public <K, V> Cache<K, V> build(CacheLoader<? super K, V> loader)
    
    • 1

    该方法返回一个Cache对象,该对象可以用于存储和获取缓存数据

    示例代码:

      public void test() {
            // 创建一个Cache对象
            Cache<String, String> cache = CacheBuilder.newBuilder()
                    .maximumSize(100) // 设置缓存的最大容量
                    .expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存项的过期时间
                    .build(new CacheLoader<String, String>() {
                        @Override
    		            public String load(String key) throws Exception {
    		                // 在缓存中没有找到对应值时,自动加载该值
    		                return "value for " + key;
    		            }
                    });
    
            try {
                // 获取缓存数据
                String value = cache.get("key");
                System.out.println(value); // 输出:Value for key
    
                // 缓存中不存在该数据时,会调用CacheLoader的load方法加载数据
                String value2 = cache.get("key2");
                System.out.println(value2); // 输出:Value for key2
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    创建了一个最大容量为100,写入后10分钟过期的缓存实例,并使用CacheLoader自动加载缓存值。当缓存中没有找到对应值时,CacheLoader的load方法会被调用,返回对应的值。


    CacheLoader类

    CacheLoader类是Guava缓存库中的一个重要类,用于定义缓存加载的逻辑。

    CacheLoader类的常用方法

    load(K key)

    作用:加载指定键的缓存值。如果缓存中不存在该键的值,则通过该方法加载并返回

    • 当缓存中不存在指定键的值时,会调用该方法。
    • 该方法用于从数据源或其他方式获取缓存的值。

    loadAll(Iterable keys)

    作用:批量加载指定键集合的缓存值。如果缓存中不存在某个键的值,则通过该方法加载

    acheLoader<String, String> cacheLoader = new CacheLoader<String, String>() {
        @Override
        public Map<String, String> loadAll(Iterable<? extends String> keys) throws Exception {
            Map<String, String> values = new HashMap<>();
            for (String key : keys) {
                // 从数据库或其他数据源加载缓存值的逻辑
                values.put(key, "Value for " + key);
            }
            return values;
        }
    };
    
    List<String> keys = Arrays.asList("key1", "key2", "key3");
    Map<String, String> values = cacheLoader.loadAll(keys);
    System.out.println(values); // Output: {key1=Value for key1, key2=Value for key2, key3=Value for key3}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    reload(K key, V oldValue)

    作用:重新加载指定键的缓存值。该方法在缓存值过期或被移除时调用,用于更新缓存值

    • 当缓存中的键值对需要被刷新或重新加载时,会调用该方法。
    • 该方法在缓存中存在旧值的情况下被调用,用于获取新值并替换旧值。
    • 通常在缓存的刷新策略中使用,例如定时刷新或手动刷新。
    import com.google.common.cache.CacheLoader;
    import com.google.common.util.concurrent.ListenableFuture;
    import com.google.common.util.concurrent.SettableFuture;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    public class MyCacheLoader<K, V> extends CacheLoader<K, V> {
        private Executor executor = Executors.newSingleThreadExecutor();
    
        @Override
        public V load(K key) throws Exception {
            // 在这里实现加载缓存的逻辑
            // 返回缓存值
            return null;
        }
    
        @Override
        public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
            SettableFuture<V> future = SettableFuture.create();
    
            executor.execute(() -> {
                try {
                    // 在这里实现重新加载缓存的逻辑
                    // 将重新加载的缓存值设置到future中
                    V newValue = null;
                    future.set(newValue);
                } catch (Exception e) {
                    // 如果重新加载缓存失败,设置异常到future中
                    future.setException(e);
                }
            });
    
            return future;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    创建了一个自定义的CacheLoader类,继承自CacheLoader。在load方法中,你可以实现加载缓存的逻辑,并返回缓存值。

    在reload方法中,使用了ListenableFuture来表示重新加载缓存的异步操作。创建了一个SettableFuture对象,用于保存重新加载的缓存值。然后,使用一个Executor来执行重新加载缓存的逻辑,将重新加载的缓存值设置到future中。如果重新加载缓存失败,我们将异常设置到future中。

    这样,就可以在CacheLoader类中使用reload方法来实现缓存的重新加载操作了。

    完整示例如下:

    import com.google.common.cache.CacheBuilder;
    import com.google.common.cache.CacheLoader;
    import com.google.common.cache.LoadingCache;
    import com.google.common.util.concurrent.ListenableFuture;
    import com.google.common.util.concurrent.ListenableFutureTask;
    import org.springframework.stereotype.Service;
    
    import java.util.concurrent.*;
    
    
    @Service
    public class CacheService {
    
        private ExecutorService executorService = Executors.newFixedThreadPool(5);
    
        private LoadingCache<String, String> cache = CacheBuilder.newBuilder()
            .maximumSize(100) // 设置缓存的最大容量
            .expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存项的过期时间
            .build(new CacheLoader<String, String>() {
    
                //当缓存中不存在指定键的值时,会调用该方法
                @Override
                public String load(String key) throws Exception {
                    // 在缓存中没有找到对应值时,自动加载该值
                    return "value for " + key;
                }
    
                //当缓存中的键值对需要被刷新或重新加载时,会调用该方法。
                //该方法在缓存中存在旧值的情况下被调用,用于获取新值并替换旧值。
               @Override
               public ListenableFuture<String> reload(String key, String oldValue) throws Exception {
    
                   ListenableFutureTask<String> futureTask =
                       ListenableFutureTask.create(
                           new Callable<String>() {
                               @Override
                               public String call() throws Exception {
                                   return load(key);
                               }
                           }
                           // 也可使用lam表达式
    //                       () -> {
    //                           return load(key);
    //                       }
                           );
                   executorService.execute(futureTask);
    
                   return futureTask;
               }
    
             }
          );
    
        /**
         * 当调用refresh方法时,Guava缓存框架会异步地刷新缓存中指定键的值。具体来说,它会调用CacheLoader的reload方法来重新加载指定键的值,并将新值存入缓存中。如果reload方法返回null
         * ,则表示该键对应的值不存在,缓存中的该键值对将被删除。
         *
         * 需要注意的是,refresh方法并不会阻塞当前线程,它会立即返回。如果需要等待刷新完成,可以使用CacheLoader的reload方法,或者使用CacheBuilder的refreshAfterWrite
         * 方法来设置缓存的自动刷新策略。
         * @param key
         */
        public void refresh (String key) {
            cache.refresh(key);
        }
    
        /**
         * ,invalidateAll()方法用于清空缓存中的所有数据。
         *
         * 具体来说,调用invalidateAll()方法会使缓存中的所有数据失效,即下一次访问这些数据时,缓存会重新加载它们。这个方法可以用于在某些情况下需要强制刷新缓存的场景,比如缓存中的数据已经过期或者需要重新加载。
         *
         * 需要注意的是,invalidateAll()
         * 方法并不会立即清空缓存中的所有数据,而是会在后台异步地清空它们。因此,在调用该方法后,如果立即访问缓存中的数据,可能会得到旧的数据。如果需要立即清空缓存中的所有数据,可以调用invalidateAll()
         * 方法后再调用cleanUp()方法,这样可以立即清空缓存中的所有数据。
         */
        public void invalidateAll() {
            cache.invalidateAll();
        }
    
    
        public void put(String key,String value) {
            cache.put(key,value);
        }
    
        /**
         * 功能:根据给定的键获取缓存中对应的值,如果缓存中不存在该键,则返回null。
         * 不会触发缓存加载器(CacheLoader)的加载操作,即不会自动加载缓存中不存在的键对应的值。
         * 适用于只需要判断缓存中是否存在某个键的情况,不需要加载新值的场景。
         *
         *  总结:`getIfPresent`方法只用于获取缓存中已存在的值,不会加载新值。
         * @param key
         * @return
         */
        public String getIfPresent(String key) {
           return cache.getIfPresent(key);
        }
    
        /**
         * 如果缓存中已经存在该键,则直接返回缓存中的值。
         * 如果缓存中不存在该键,则通过加载器加载新值,并将新值放入缓存中,然后返回新值。
         * 适用于需要加载新值的场景
         *
         * 总结:`get`方法可以获取缓存中已存在的值,也可以加载新值并放入缓存中。
         * @param key
         * @return
         * @throws ExecutionException
         */
        public String get(String key) throws ExecutionException {
            return cache.get(key);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111

    Guava线程池

    Guava的线程池主要包含以下几个类:

    1. ListeningExecutorService:一个可监听的ExecutorService,可以在任务执行完成时触发回调函数。

    2. MoreExecutors:提供了一些常用的ExecutorService的工厂方法,例如newDirectExecutorService()、newFixedThreadPool()、newCachedThreadPool()等。

    3. ThreadFactoryBuilder:一个用于创建线程工厂的Builder类,可以设置线程名、优先级、是否为守护线程等属性。

    import com.google.common.util.concurrent.*;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.Executors;
    
    public class GuavaThreadPoolDemo {
    
        public static void main(String[] args) throws Exception {
            // 创建线程池
            ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
                    Executors.newFixedThreadPool(10, new ThreadFactoryBuilder()
                            .setNameFormat("guava-pool-%d")
                            .setDaemon(true)
                            .build()));
    
            // 提交任务
            ListenableFuture<String> future = executorService.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    // 模拟耗时操作
                    Thread.sleep(1000);
                    return "Hello, Guava!";
                }
            });
    
            // 添加回调函数
            Futures.addCallback(future, new FutureCallback<String>() {
                @Override
                public void onSuccess(String result) {
                    System.out.println(result);
                }
    
                @Override
                public void onFailure(Throwable t) {
                    t.printStackTrace();
                }
            }, executorService);
    
            // 关闭线程池
            executorService.shutdown();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    使用MoreExecutors工厂方法创建了一个固定大小的线程池,然后使用ListeningExecutorService包装它,使其可以监听任务执行完成事件。接着,提交了一个Callable任务,并使用Futures.addCallback方法添加了一个回调函数,当任务执行完成时会自动触发回调函数。最后,关闭了线程池。


    Guava的线程池和Guava缓存的结合示例代码

    import com.google.common.cache.CacheLoader;
    import com.google.common.util.concurrent.ListenableFuture;
    import com.google.common.util.concurrent.SettableFuture;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    public class MyCacheLoader<K, V> extends CacheLoader<K, V> {
        // 创建线程池
        ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
                Executors.newFixedThreadPool(10, new ThreadFactoryBuilder()
                        .setNameFormat("guava-pool-%d")
                        .setDaemon(true)
                        .build()));
    
        @Override
        public V load(K key) throws Exception {
            // 在这里实现加载缓存的逻辑
            // 返回缓存值
            return null;
        }
    
        @Override
        public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
            SettableFuture<V> future = SettableFuture.create();
    
            executorService.execute(() -> {
                try {
                    // 在这里实现重新加载缓存的逻辑
                    // 将重新加载的缓存值设置到future中
                    V newValue = null;
                    future.set(newValue);
                } catch (Exception e) {
                    // 如果重新加载缓存失败,设置异常到future中
                    future.setException(e);
                }
            });
    
            return future;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
  • 相关阅读:
    ts泛型,映射,条件类型和类型提取infer和一些常用工具库的说明
    【Java小白福利】Java面试、学习教程合集!
    Add the installation prefix of “Qt5“ to CMAKE_PREFIX_PATH or set “Qt5_DIR“解决
    从 几 个应用入手 了解为什么灵魂绑定代币将为 DeFi 带来大规模采用
    Vue之 el-input文本框拿到大量数据时自动换行显示
    计算组格式表达式的妙用
    react 18 createRoot没有渲染出DOM元素
    Windows+Visual stdio+CUDA编程方式及测试
    用C语言实现双向链表
    STM32cubeIDE 更改Repository folder
  • 原文地址:https://blog.csdn.net/weixin_42594143/article/details/133072222