zoukankan      html  css  js  c++  java
  • 缓存工具CacheUtil

    缓存工具CacheUtil - 并发环境的缓存值存取

    目的

    • 适合并发环境的缓存值存取
    • 读取缓存值时,只需关注数据来源。不用再关注将源数据存入缓存等后续处理。
    • 应用程序N次读取数据时,数据源读取一次,缓存读取N-1次。

    设计

    当从缓存查找失败,则去数据源获取。获取成功,存入缓存并返回。

    实现

    • Sourcable

      /**
       * 可溯源的。可从数据源获取数据
       */
      public interface Sourcable {
          /**
           * 从数据源获取
           */
          <T> T get();
      }
      
    • CacheUtil

      public class CacheUtil {
          private static final transient Log logger = LogFactory.getLog(CacheUtil.class);
      
      //    @Resource(name = "memCacheServiceImpl")
          private static CacheService cache;
      
          /**
           * 获取value
           * 如果从缓存查找失败,则尝试从数据源获取,获取成功,存入缓存并返回。
           * @param key
           * @param source Sourcable的实现,用于读取源数据。
           * @param isShort 是否(短时间)暂存
           */
          public static <T> T get(String key, Sourcable source, boolean isShort) {
              logger.debug("-> [" + Thread.currentThread().getId() + "] Enter");
              T value = get(key);
              if (value == null && source != null) {
                  // 缓存查找失败,尝试从数据源获取
                  logger.debug("-> [" + Thread.currentThread().getId() + "] try to Lock");
                  key = key.intern();
                  synchronized (key) {
                      logger.debug("-> [" + Thread.currentThread().getId() + "] Locked");
                      value = get(key);
                      // 双重检查。防止多线程重复从数据源读取
                      if (value == null) {
                          // 从数据源获取
                          value = source.get();
                          if (value != null) {
                              // 存入缓存
                              if (isShort) {
                                  // 暂存cache
                                  put4short(key, value);
                              } else {
                                  // 常规存入cache,正常有效期
                                  put(key, value);
                              }
                          }
                          logger.debug("-> [" + Thread.currentThread().getId() + "] Loaded");
                      }
                  }
              }
              logger.debug("-> [" + Thread.currentThread().getId() + "] get: " + value);
              return value;
          }
      
          // 其他代码省略..
      
      }
      

    测试

    • CacheUtilTest
      public class CacheUtilTest extends SpringTest {
      
          @Test
          public void get() throws Exception {
              final Long resourceId = 173L;
              final String cacheKey = Resource.class.getName() + '#' + resourceId;
      
              // 清除
              CacheUtil.delete(cacheKey);
      
              // 1000个线程并发存取
              int num = 1000;
              Executor executor = Executors.newFixedThreadPool(num);
              final CountDownLatch latch = new CountDownLatch(num);
              for (int i = 0; i < num; i++) {
                  executor.execute(() -> {
                      // new String(cacheKey)。 用于验证不同key对象时,同步锁的有效性
                      String key = new String(cacheKey);
                      // 读取缓存值时,只需关注数据来源。不用再关注将源数据存入缓存。
                      Resource resource = CacheUtil.get(key, new Sourcable() {
                              @Override
                              public <T> T get() {
                                  List<Resource> resources = DAOUtil.findByHql("FROM Resource WHERE resourceId=" + resourceId);
                                  if (CollectionUtils.isNotEmpty(resources)) {
                                      return resources.get(0);
                                  }
                                  return null;
                              }
                          });
                      latch.countDown();
                  });
              }
              latch.await();
          }
      }
      

    附录

    • SpringTest

      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration("classpath:context/spring.xml")
      public abstract class SpringTest {}
      
    • 结果
      10个线程运行结果:

    [PAY][DEBUG] 2016-10-14 15:14:20,526 (CacheUtil.java:46).get - [pool-1-thread-2] 14030 
    -> [29] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-8] 14031 
    -> [35] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-10] 14031 
    -> [37] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,528 (CacheUtil.java:46).get - [pool-1-thread-9] 14032 
    -> [36] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-4] 14031 
    -> [31] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-7] 14031 
    -> [34] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,526 (CacheUtil.java:46).get - [pool-1-thread-6] 14030 
    -> [33] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,526 (CacheUtil.java:46).get - [pool-1-thread-5] 14030 
    -> [32] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,555 (CacheUtil.java:49).get - [pool-1-thread-7] 14059 
    -> [34] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,557 (CacheUtil.java:53).get - [pool-1-thread-7] 14061 
    -> [34] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,561 (CacheUtil.java:49).get - [pool-1-thread-5] 14065 
    -> [32] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,555 (CacheUtil.java:49).get - [pool-1-thread-4] 14059 
    -> [31] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,547 (CacheUtil.java:49).get - [pool-1-thread-10] 14051 
    -> [37] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,547 (CacheUtil.java:49).get - [pool-1-thread-8] 14051 
    -> [35] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,547 (CacheUtil.java:49).get - [pool-1-thread-9] 14051 
    -> [36] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,547 (CacheUtil.java:49).get - [pool-1-thread-2] 14051 
    -> [29] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,528 (CacheUtil.java:46).get - [pool-1-thread-1] 14032 
    -> [28] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-3] 14031 
    -> [30] Enter
    [PAY][DEBUG] 2016-10-14 15:14:20,565 (CacheUtil.java:49).get - [pool-1-thread-6] 14069 
    -> [33] try to Lock
    Hibernate: FROM Resource WHERE resourceId=173
    [PAY][DEBUG] 2016-10-14 15:14:20,595 (CacheUtil.java:49).get - [pool-1-thread-1] 14099 
    -> [28] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,599 (CacheUtil.java:49).get - [pool-1-thread-3] 14103 
    -> [30] try to Lock
    [PAY][DEBUG] 2016-10-14 15:14:20,639 (CacheUtil.java:72).get - [pool-1-thread-7] 14143 
    -> [34] Loaded
    [PAY][DEBUG] 2016-10-14 15:14:20,639 (CacheUtil.java:53).get - [pool-1-thread-3] 14143 
    -> [30] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,639 (CacheUtil.java:76).get - [pool-1-thread-7] 14143 
    -> [34] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,645 (CacheUtil.java:53).get - [pool-1-thread-1] 14149 
    -> [28] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,645 (CacheUtil.java:76).get - [pool-1-thread-3] 14149 
    -> [30] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,651 (CacheUtil.java:76).get - [pool-1-thread-1] 14155 
    -> [28] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,651 (CacheUtil.java:53).get - [pool-1-thread-6] 14155 
    -> [33] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,658 (CacheUtil.java:76).get - [pool-1-thread-6] 14162 
    -> [33] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,658 (CacheUtil.java:53).get - [pool-1-thread-2] 14162 
    -> [29] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,665 (CacheUtil.java:76).get - [pool-1-thread-2] 14169 
    -> [29] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,665 (CacheUtil.java:53).get - [pool-1-thread-9] 14169 
    -> [36] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,671 (CacheUtil.java:76).get - [pool-1-thread-9] 14175 
    -> [36] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,671 (CacheUtil.java:53).get - [pool-1-thread-8] 14175 
    -> [35] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,676 (CacheUtil.java:76).get - [pool-1-thread-8] 14180 
    -> [35] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,676 (CacheUtil.java:53).get - [pool-1-thread-10] 14180 
    -> [37] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,681 (CacheUtil.java:76).get - [pool-1-thread-10] 14185 
    -> [37] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,681 (CacheUtil.java:53).get - [pool-1-thread-4] 14185 
    -> [31] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,686 (CacheUtil.java:76).get - [pool-1-thread-4] 14190 
    -> [31] get: /uploadfiles/resource/2014122313575119.png
    [PAY][DEBUG] 2016-10-14 15:14:20,686 (CacheUtil.java:53).get - [pool-1-thread-5] 14190 
    -> [32] Locked
    [PAY][DEBUG] 2016-10-14 15:14:20,694 (CacheUtil.java:76).get - [pool-1-thread-5] 14198 
    -> [32] get: /uploadfiles/resource/2014122313575119.png
    

    只有一个id为34的线程Loaded,其余线程均等待 线程34 处理(从数据库查询结果并存入缓存)完成后,再查找缓存从而命中数据。

    问题

    • 数据源失效问题
      当数据源不存在数据时,一直缓存失败,则会一直从数据源获取数据。从而造成数据源负担。
      为防止这个情况,可以再进一步做个过滤保障机制。
  • 相关阅读:
    Div+CSS+JQuery实现选项卡,即通过点击不同的li跳转到不同的div中显示不同的内容或者执行不同的操作。
    js如何获取点击<li>标签里的内容值
    python requests库爬取网页小实例:ip地址查询
    python requests库爬取网页小实例:爬取网页图片
    python requests库网页爬取小实例:百度/360搜索关键词提交
    python requests库网页爬取小实例:亚马逊商品页面的爬取
    python使用requests库爬取网页的小实例:爬取京东网页
    hibernate 的入门
    html
    事务的入门(mysql)
  • 原文地址:https://www.cnblogs.com/final/p/5960703.html
Copyright © 2011-2022 走看看