精华内容
下载资源
问答
  • java创建本地缓存,模拟redis的使用 在一般的小项目中,数据量不大.但是有的时候需要使用缓存记录一些标识或者票据之类的,比如我这边想实现,可以记录系统同时在线的用户数据,或者对其他数据的缓存记录,减少DBA请求 1. ...

    java创建本地缓存,模拟redis的使用

    在一般的小项目中,数据量不大.但是有的时候需要使用缓存记录一些标识或者票据之类的,比如我这边想实现,可以记录系统同时在线的用户数据,或者对其他数据的缓存记录,减少DBA请求

    1. 创建缓存实体类

    package com.adingxiong.cft.entity;
    
    import java.io.Serializable;
    
    /**  * @author xiongc  * @date 2020/07/28  */ public class CacheEntity implements Serializable {  private static final long serialVersionUID = -107853226360392750L;  /**  * 值  */  private Object value;   /**  * 保存的时间戳  */  private long gmtModify;   /**  * 过期时间  */  private int expire;   public Object getValue() {  return value;  }   public void setValue(Object value) {  this.value = value;  }   public long getGmtModify() {  return gmtModify;  }   public void setGmtModify(long gmtModify) {  this.gmtModify = gmtModify;  }   public int getExpire() {  return expire;  }   public void setExpire(int expire) {  this.expire = expire;  }   public CacheEntity(Object value, long gmtModify, int expire) {  super();  this.value = value;  this.gmtModify = gmtModify;  this.expire = expire;  } }   
    

    2. 创建本地缓存工具类

    package com.adingxiong.cft.cache;
    
    import com.adingxiong.cft.entity.CacheEntity;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils;  import java.io.*; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit;  /**  * @ClassName LocalCache  * @Description 本地缓存 代替redis 实现简单数据记录  * @Author xiongchao  * @Date 2020/7/28 16:14  **/ public class LocalCache {   private static final Logger log = LoggerFactory.getLogger(LocalCache.class);   /**  * 默认的缓存容量  */  private static final int DEFAULT_CAPACITY = 512;  /**  * 最大容量  */  private static final int MAX_CAPACITY = 100000;  /**  * 刷新缓存的频率  */  private static final int MONITOR_DURATION = 2;   // 启动监控线程  static {  new Thread(new TimeoutTimerThread()).start();  }   // 内部类方式实现单例  private static class LocalCacheInstance{  private static final LocalCache INSTANCE=new LocalCache();   }  public static LocalCache getInstance() {  return LocalCacheInstance.INSTANCE;  }   private LocalCache() {  }   /**  * 使用默认容量创建一个Map  */  private static Map<String, CacheEntity> cache = new ConcurrentHashMap<>(DEFAULT_CAPACITY);   /**  * 将key-value 保存到本地缓存并制定该缓存的过期时间  *  * @param key  * @param value  * @param expireTime 过期时间,如果是-1 则表示永不过期  * @return  */  public <T> boolean putValue(String key, T value, int expireTime) {  return putCloneValue(key, value, expireTime);  }   /**  * 将值通过序列化clone 处理后保存到缓存中,可以解决值引用的问题  *  * @param key  * @param value  * @param expireTime  * @return  */  private <T> boolean putCloneValue(String key, T value, int expireTime) {  try {  if (cache.size() >= MAX_CAPACITY) {  return false;  }  // 序列化赋值  CacheEntity entityClone = clone(new CacheEntity(value, System.nanoTime(), expireTime));  cache.put(key, entityClone);  return true;  } catch (Exception e) {  log.error("添加缓存失败:{}", e.getMessage());  }  return false;  }   /**  * 序列化 克隆处理  *  * @param object  * @return  */  private <E extends Serializable> E clone(E object) {  E cloneObject = null;  try {  ByteArrayOutputStream baos = new ByteArrayOutputStream();  ObjectOutputStream oos = new ObjectOutputStream(baos);  oos.writeObject(object);  oos.close();  ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());  ObjectInputStream ois = new ObjectInputStream(bais);  cloneObject = (E) ois.readObject();  ois.close();  } catch (Exception e) {  log.error("缓存序列化失败:{}", e.getMessage());  }  return cloneObject;  }   /**  * 从本地缓存中获取key对应的值,如果该值不存则则返回null  *  * @param key  * @return  */  public Object getValue(String key) {  if(CollectionUtils.isEmpty(cache)){  return null;  }  return cache.get(key).getValue();  }   /**  * 清空所有  */  public void clear() {  cache.clear();  }   /**  * 过期处理线程  */  static class TimeoutTimerThread implements Runnable {  @Override  public void run() {  while (true) {  try {  TimeUnit.SECONDS.sleep(MONITOR_DURATION);  checkTime();  } catch (Exception e) {  log.error("过期缓存清理失败:{}", e.getMessage());  }  }  }   /**  * 过期缓存的具体处理方法  *  * @throws Exception  */  private void checkTime() throws Exception {  // 开始处理过期  for (String key : cache.keySet()) {  CacheEntity tce = cache.get(key);  long timoutTime = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - tce.getGmtModify());  // 过期时间 : timoutTime  if (tce.getExpire() > timoutTime) {  continue;  }  log.info(" 清除过期缓存 : " + key);  //清除过期缓存和删除对应的缓存队列  cache.remove(key);  }  }  } } 
    

    3. 测试

    写一个简单的controller

    @GetMapping("/cache")
        public Object testCache(String name){
            Object a = LocalCache.getInstance().getValue("name");
            if(a == null){
     LocalCache.getInstance().putValue("name" ,"张三",10);  return "未读取到缓存数据";  }  return "读取到缓存数据,数据为:" + a;  } 
    

    第一次调用

    image
    image

    第二次调用 image

    同时服务那边会根据你设定的过期时间去定期启动线程清理缓存数据

    image
    image

    本文使用 mdnice 排版

    展开全文
  • Java实现本地缓存

    千次阅读 2019-12-26 09:31:02
    java实现本地缓存 缓存 缓存,将程序或系统中重复用到的数据缓存在内存中,加快访问速度,减少系统开销,提高系统效率的方案。 数据存储方式主要分2种: 文件 数据保存在文件中,做持久化。 内存 数据保存在内存中...

    java实现本地缓存

    缓存

    缓存,将程序或系统中重复用到的数据缓存在内存中,加快访问速度,减少系统开销,提高系统效率的方案。

    数据存储方式主要分2种:

    1. 文件 数据保存在文件中,做持久化。
    2. 内存 数据保存在内存中,也就是创建一个静态内存区域,本文中数据保存在静态区域,使用MAP保存key\value数据。

    开源缓存框架

    • Redis
      Redis是基于内存、可持久化的日志型、Key-Value数据库高性能存储系统,并提供多种语言的API.
    • memcached
      是一个自由开源的,高性能,分布式内存对象缓存系统。基于内存的key-value存储,用来存储小块的任意数据(字符串、对象)。

    解决场景

    目前在开发的项目中,不希望引入太多外部服务,引入分布式缓存框架例如Redis,还需要部署与维护,这会大大增加对项目的维护成本。

    Java实现本地缓存框架逻辑

    框架基本定义

    • 数据存储格式 key-value,仅针对字符串缓存。
    • 数据有效性,增加超时时间失效逻辑
    • 失效策略:定期删除与懒惰淘汰并行

    定期删除策略

    定期删除策略是每隔一段时间检测已过期的缓存,并且降之删除。这个策略的优点是能够确保过期的缓存都会被删除。同时也存在着缺点,过期的缓存不一定能够及时的被删除,这跟我们设置的定时频率有关系,另一个缺点是如果缓存数据较多时,每次检测也会给 cup 带来不小的压力。

    懒惰淘汰策略

    懒惰淘汰策略是在使用缓存时,先判断缓存是否过期,如果过期将它删除,并且返回空。这个策略的优点是只有在查找的时候,才判断是否过期,对 CUP 影响较小。同时这种策略有致命的缺点,当存入了大量的缓存,这些缓存都没有被使用并且已过期,都将成为无效缓存,这些无效的缓存将占用你大量的内存空间,最后导致服务器内存溢出。
    我们简单的了解了一下 Redis 的两种过期缓存处理策略,每种策略都存在自己的优缺点。所以我们在使用过程中,可以将两种策略组合起来,结合效果还是非常理想的。

    逻辑顺序

    1. 定义缓存map, 定义定时移除时效数据的任务,在static代码块中初始化。
    2. 定义缓存对象,包含值和过期时间。
    3. 定义定时移除时效数据的异步任务。
    4. 实现俩种时效策略移除数据的逻辑 remove + removeAll.
    5. 实现put与get方法。

    框架中用到的定时任务模块

    private final static ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    executor.scheduleAtFixedRate(new Task(), INITIAL_DELAY_TIME, PERIOD_TIME, TimeUnit.SECONDS);

    初始化线程池,启动定时执行任务scheduleAtFixedRate

    1. new Task() 定时任务执行的具体业务逻辑
    2. INITIAL_DELAY_TIME 启动后延迟时间执行(秒)
    3. PERIOD_TIME 每次执行间隔(秒)

    执行结果

    定时任务策略:延迟5秒执行,每隔5秒执行一次 (实际失效策略时间可以放长一点)

    首先添加缓存数据

    LocalCache.put("a", "10a", 7);
    LocalCache.put("b", "10b", 12);
    LocalCache.put("c", "10c", 30);
    2019-12-25 21:00:19,914 [http-nio-9060-exec-1] INFO LocalCache 62 - 添加缓存,key=a, value=10a, expire=7秒
    2019-12-25 21:00:19,915 [http-nio-9060-exec-1] INFO LocalCache 62 - 添加缓存,key=b, value=10b, expire=12秒
    2019-12-25 21:00:19,915 [http-nio-9060-exec-1] INFO LocalCache 62 - 添加缓存,key=c, value=10c, expire=30秒
    

    定时任务执行第二次 10s后,移除缓存数据a

    2019-12-25 21:00:24,919 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={a=LocalCache.Cache(value=10a, expire=1577278826915), b=LocalCache.Cache(value=10b, expire=1577278831915), c=LocalCache.Cache(value=10c, expire=1577278849915)}
    2019-12-25 21:00:29,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={a=LocalCache.Cache(value=10a, expire=1577278826915), b=LocalCache.Cache(value=10b, expire=1577278831915), c=LocalCache.Cache(value=10c, expire=1577278849915)}
    2019-12-25 21:00:29,919 [pool-3-thread-1] INFO LocalCache 94 - 定期删除策略: 移除超时失效数据, key=a, value=10a, time=1577278826915
    

    定时任务执行第三次 15s后,移除缓存数据b

    2019-12-25 21:00:34,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={b=LocalCache.Cache(value=10b, expire=1577278831915), c=LocalCache.Cache(value=10c, expire=1577278849915)}
    2019-12-25 21:00:34,919 [pool-3-thread-1] INFO LocalCache 94 - 定期删除策略: 移除超时失效数据, key=b, value=10b, time=1577278831915
    

    定时任务执行第六次 30s后,移除缓存数据c

    2019-12-25 21:00:39,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={c=LocalCache.Cache(value=10c, expire=1577278849915)}
    2019-12-25 21:00:44,919 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={c=LocalCache.Cache(value=10c, expire=1577278849915)}
    2019-12-25 21:00:49,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={c=LocalCache.Cache(value=10c, expire=1577278849915)}
    2019-12-25 21:00:49,919 [pool-3-thread-1] INFO LocalCache 94 - 定期删除策略: 移除超时失效数据, key=c, value=10c, time=1577278849915
    2019-12-25 21:00:54,918 [pool-3-thread-1] INFO LocalCache 89 - 定期删除策略: 开始执行, store={}
    

    在这里插入图片描述

    源码

    package com.core.mall.config.cache;
    
    import lombok.Data;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.concurrent.*;
    
    /**
     * 本地缓存服务
     * key = 字符串 | value = 字符串 | expire = 秒(有效时间)
     *
     * put 添加缓存
     * get 读取缓存值
     *
     * 失效策略:
     *  1. 定期删除策略:启动1个线程,每2分钟扫描一次,超时数据移除
     *  2. 懒惰淘汰策略:每次访问时校验有效性,如果失效移除
     */
    public class LocalCache {
        private final static Logger logger = LoggerFactory.getLogger(LocalCache.class);
    
        /**
         * 启动开始后延迟5秒执行时效策略
         */
        private static final int INITIAL_DELAY_TIME = 5;
        /**
         * 执行时效策略间隔时间
         */
        private static final int PERIOD_TIME = 5;
        /**
         * 本地缓存map
         */
        private static ConcurrentHashMap<String, Cache> store;
        /**
         * 执行时效策略线程池
         */
        private final static ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    
        /*
         * 静态代码块
         *
         * 初始化缓存map
         * 添加时效策略定时线程任务
         */
        static {
            store = new ConcurrentHashMap<>();
            executor.scheduleAtFixedRate(new Task(), INITIAL_DELAY_TIME, PERIOD_TIME, TimeUnit.SECONDS);
        }
    
        public static void put(String key, String value) {
            put(key, value, 0);
        }
    
        /**
         * 设置缓存
         * @param key 唯一key
         * @param value 值
         * @param expire 超时时间-单位(s/秒)
         */
        public static void put(String key, String value, long expire) {
            logger.info("添加缓存,key={}, value={}, expire={}秒", key, value, expire);
            if (expire > 0) {
                store.put(key, new Cache(value, expire));
            } else {
                store.put(key, new Cache(value));
            }
        }
    
        public static String get(String key) {
            Cache cache = store.get(key);
            if (cache == null) {
                return null;
            }
    
            if (cache.getExpire() > 0 && cache.getExpire() < System.currentTimeMillis()) {
                remove(key);
                return null;
            }
            return store.get(key).getValue();
        }
    
        private static void remove(String key) {
            Cache cache = store.remove(key);
            logger.info("懒惰淘汰策略: 移除超时失效数据, cache={}", cache);
        }
    
        private static void removeAll() {
            logger.info("定期删除策略: 开始执行, store={}", store);
            for (String key : store.keySet()) {
                Cache cache = store.get(key);
                if (cache.getExpire() > 0 && cache.getExpire() < System.currentTimeMillis()) {
                    store.remove(key);
                    logger.info("定期删除策略: 移除超时失效数据, key={}, value={}, time={}", key, cache.getValue(), cache.getExpire());
                }
            }
        }
    
        /**
         * 定时移除时效数据任务
         */
        private static class Task implements Runnable {
            @Override
            public void run() {
                try {
                    LocalCache.removeAll();
                } catch (Exception e) {
                    logger.info("定期删除策略异常", e);
                }
            }
        }
    
        /**
         * 本地缓存对象
         */
        @Data
        private static class Cache {
            private String value;
            private long expire = 0;
    
            Cache(String value, long expire) {
                this.value = value;
                this.expire = System.currentTimeMillis() + expire * 1000;
            }
    
            Cache(String value) {
                this.value = value;
            }
        }
    }
    
    

    联系方式

    如果对你有帮助,可以关注作者支持一下,每天会不定时回复留言(有任何问题都可以留言哦)。

    微信公众号
    浮生一境
    展开全文
  • 实现 Java 本地缓存,该从这几点开始

    千次阅读 多人点赞 2019-09-11 09:11:17
    对于这些工具,我想大家肯定都非常熟悉,所以今天我们不聊它们,我们来聊一聊如何实现本地缓存。参考上面几种工具,要实现一个较好的本地缓存,平头哥认为要从以下三个方面开始。 1、存储集合的选择 实现本地缓存,...

    缓存,我相信大家对它一定不陌生,在项目中,缓存肯定是必不可少的。市面上有非常多的缓存工具,比如 Redis、Guava Cache 或者 EHcache。对于这些工具,我想大家肯定都非常熟悉,所以今天我们不聊它们,我们来聊一聊如何实现本地缓存。参考上面几种工具,要实现一个较好的本地缓存,平头哥认为要从以下三个方面开始。

    1、存储集合的选择

    实现本地缓存,存储容器肯定是 key/value 形式的数据结构,在 Java 中,也就是我们常用的 Map 集合。Map 中有 HashMap、Hashtable、ConcurrentHashMap 几种供我们选择,如果不考虑高并发情况下数据安全问题,我们可以选择HashMap,如果考虑高并发情况下数据安全问题,我们可以选择 Hashtable、ConcurrentHashMap 中的一种集合,但是我们优先选择 ConcurrentHashMap,因为 ConcurrentHashMap 的性能比 Hashtable 要好。

    2、过期缓存处理

    因为缓存直接存储在内存中,如果我们不处理过期缓存,内存将被大量无效缓存占用,这不是我们想要的,所以我们需要清理这些失效的缓存。过期缓存处理可以参考 Redis 的策略来实现,Redis 采用的是定期删除 + 懒惰淘汰策略。

    定期删除策略

    定期删除策略是每隔一段时间检测已过期的缓存,并且降之删除。这个策略的优点是能够确保过期的缓存都会被删除。同时也存在着缺点,过期的缓存不一定能够及时的被删除,这跟我们设置的定时频率有关系,另一个缺点是如果缓存数据较多时,每次检测也会给 cup 带来不小的压力。

    懒惰淘汰策略

    懒惰淘汰策略是在使用缓存时,先判断缓存是否过期,如果过期将它删除,并且返回空。这个策略的优点是只有在查找的时候,才判断是否过期,对 CUP 影响较。同时这种策略有致命的缺点,当存入了大量的缓存,这些缓存都没有被使用并且已过期,都将成为无效缓存,这些无效的缓存将占用你大量的内存空间,最后导致服务器内存溢出。

    我们简单的了解了一下 Redis 的两种过期缓存处理策略,每种策略都存在自己的优缺点。所以我们在使用过程中,可以将两种策略组合起来,结合效果还是非常理想的。

    3、缓存淘汰策略

    缓存淘汰跟过期缓存处理要区别开来,缓存淘汰是指当我们的缓存个数达到我们指定的缓存个数时,毕竟我们的内存不是无限的。如果我们需要继续添加缓存的话,我们就需要在现有的缓存中根据某种策略淘汰一些缓存,给新添加的缓存腾出位置,下面一起来认识几种常用的缓存淘汰策略。

    先进先出策略

    最先进入缓存的数据在缓存空间不够的情况下会被优先被清除掉,以腾出新的空间接受新的数据。该策略主要比较缓存元素的创建时间。在一些对数据实效性要求比较高的场景下,可考虑选择该类策略,优先保障最新数据可用。

    最少使用策略

    无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间。该策略主要比较元素的hitCount(命中次数),在保证高频数据有效性场景下,可选择这类策略。

    最近最少使用策略

    无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素释放空间。该策略主要比较缓存最近一次被get使用时间。在热点数据场景下较适用,优先保证热点数据的有效性。

    随机淘汰策略

    无论是否过期,随机淘汰某个缓存,如果对缓存数据没有任何要求,可以考虑使用该策略。

    不淘汰策略

    当缓存达到指定值之后,不淘汰任何缓存,而是不能新增缓存,直到有缓存淘汰时,才能继续添加缓存。

    上面是实现本地缓存需要考虑的三个点,看完我们应该知该如何实现一个本地缓存了,不妨我们一起来实现一个本地缓存。

    实现本地缓存

    在该 Demo 中,我们采用 ConcurrentHashMap 作为存储集合,这样即使在高并发的情况下,我们也能够保证缓存的安全。过期缓存处理在这里我只使用了定时删除策略,并没有使用定时删除 + 懒惰淘汰策略,你可以自己动手尝试一下使用这两种策略进行过期缓存处理。在缓存淘汰方面,我在这里采用了最少使用策略。好了,技术选型都知道了,我们一起来看看代码实现。

    缓存对象类

    public class Cache implements Comparable<Cache>{
        // 键
        private Object key;
        // 缓存值
        private Object value;
        // 最后一次访问时间
        private long accessTime;
        // 创建时间
        private long writeTime;
        // 存活时间
        private long expireTime;
        // 命中次数
        private Integer hitCount;
        ...getter/setter()...
    

    添加缓存

    /**
     * 添加缓存
     *
     * @param key
     * @param value
     */
    public void put(K key, V value,long expire) {
        checkNotNull(key);
        checkNotNull(value);
        // 当缓存存在时,更新缓存
        if (concurrentHashMap.containsKey(key)){
            Cache cache = concurrentHashMap.get(key);
            cache.setHitCount(cache.getHitCount()+1);
            cache.setWriteTime(System.currentTimeMillis());
            cache.setAccessTime(System.currentTimeMillis());
            cache.setExpireTime(expire);
            cache.setValue(value);
            return;
        }
        // 已经达到最大缓存
        if (isFull()) {
            Object kickedKey = getKickedKey();
            if (kickedKey !=null){
                // 移除最少使用的缓存
                concurrentHashMap.remove(kickedKey);
            }else {
                return;
            }
        }
        Cache cache = new Cache();
        cache.setKey(key);
        cache.setValue(value);
        cache.setWriteTime(System.currentTimeMillis());
        cache.setAccessTime(System.currentTimeMillis());
        cache.setHitCount(1);
        cache.setExpireTime(expire);
        concurrentHashMap.put(key, cache);
    }
    

    获取缓存

    /**
     * 获取缓存
     *
     * @param key
     * @return
     */
    public Object get(K key) {
        checkNotNull(key);
        if (concurrentHashMap.isEmpty()) return null;
        if (!concurrentHashMap.containsKey(key)) return null;
        Cache cache = concurrentHashMap.get(key);
        if (cache == null) return null;
        cache.setHitCount(cache.getHitCount()+1);
        cache.setAccessTime(System.currentTimeMillis());
        return cache.getValue();
    }
    

    获取最少使用的缓存

        /**
         * 获取最少使用的缓存
         * @return
         */
        private Object getKickedKey() {
            Cache min = Collections.min(concurrentHashMap.values());
            return min.getKey();
        }
    

    过期缓存检测方法

    /**
     * 处理过期缓存
     */
    class TimeoutTimerThread implements Runnable {
        public void run() {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(60);
                    expireCache();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 创建多久后,缓存失效
         *
         * @throws Exception
         */
        private void expireCache() throws Exception {
            System.out.println("检测缓存是否过期缓存");
            for (Object key : concurrentHashMap.keySet()) {
                Cache cache = concurrentHashMap.get(key);
                long timoutTime = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()
                        - cache.getWriteTime());
                if (cache.getExpireTime() > timoutTime) {
                    continue;
                }
                System.out.println(" 清除过期缓存 : " + key);
                //清除过期缓存
                concurrentHashMap.remove(key);
            }
        }
    }
    

    上面是主要代码,完整的代码我已经上传至 GitHub

    本文从实现本地缓存的设计角度,一起简单的探讨了一下实现本地缓存需要注意的地方,其实这些也是缓存的核心技术,不管是 Redis、Guava Cache 还是 EHcache或者其他缓存工具,它们在实现原理上,跟我们本地缓存的实现原理都差不多。只要我们理解了本地缓存的实现原理,在去学习这些缓存工具,我相信还是会比较轻松的。

    文章不足之处,望大家多多指点,共同学习,共同进步

    最后

    打个小广告,欢迎扫码关注微信公众号:「平头哥的技术博文」,一起进步吧。
    平头哥的技术博文

    展开全文
  • java本地缓存

    2020-12-23 15:24:23
    实现本地缓存,存储容器肯定是 key/value 形式的数据结构,在 Java 中,也就是我们常用的 Map 集合。Map 中有 HashMap、Hashtable、ConcurrentHashMap 几种供我们选择,如果不考虑高并发情况下数据安全问题,我们...

    项目中缓存必不可少,缓存工具也非常多,例如Redis、Guava Cache、JBoss Cache 、或者 EHcache。

    本地缓存如何实现工具中的缓存功能。主要三个方面

    1、存储集合的选择

    实现本地缓存,存储容器肯定是 key/value 形式的数据结构,在 Java 中,也就是我们常用的 Map 集合。Map 中有 HashMap、Hashtable、ConcurrentHashMap 几种供我们选择,如果不考虑高并发情况下数据安全问题,我们可以选择HashMap,如果考虑高并发情况下数据安全问题,我们可以选择 Hashtable、ConcurrentHashMap 中的一种集合,但是我们优先选择 ConcurrentHashMap,因为 ConcurrentHashMap 的性能比 Hashtable 要好。

    为什么 ConcurrentHashMap性能优于 Hashtable

    ConcurrentHashMap里使用了Segment分段锁+HashEntry,而HashTable用的是Syncronized锁全部,所有线程竞争一把锁。

    Segment分段锁继承ReentrantLock,在并发数高的时候,ReentrantLock比Syncronized总体开销要小一些。

    2、过期缓存处理

    因为缓存直接存储在内存中,如果我们不处理过期缓存,内存将被大量无效缓存占用,这不是我们想要的,所以我们需要清理这些失效的缓存。过期缓存处理可以参考 Redis 的策略来实现,Redis 采用的是定期删除 + 懒惰淘汰策略。

    这边简单介绍一下redis中的LFU跟LRU

           LFU: 碰了多少次

           LRU:多久没有碰

    定期删除策略:

    定期删除策略是每隔一段时间检测已过期的缓存,并且降之删除。这个策略的优点是能够确保过期的缓存都会被删除。同时也存在着缺点,过期的缓存不一定能够及时的被删除,这跟我们设置的定时频率有关系,另一个缺点是如果缓存数据较多时,每次检测也会给 cup 带来不小的压力。

    懒惰淘汰策略:

    懒惰淘汰策略是在使用缓存时,先判断缓存是否过期,如果过期将它删除,并且返回空。这个策略的优点是只有在查找的时候,才判断是否过期,对 CUP 影响较小。同时这种策略有致命的缺点,当存入了大量的缓存,这些缓存都没有被使用并且已过期,都将成为无效缓存,这些无效的缓存将占用你大量的内存空间,最后导致服务器内存溢出。

    我们简单的了解了一下 Redis 的两种过期缓存处理策略,每种策略都存在自己的优缺点。所以我们在使用过程中,可以将两种策略组合起来,结合效果还是非常理想的。

    3、缓存淘汰策略

    先进先出策略--

    最先进入缓存的数据在缓存空间不够的情况下会被优先被清除掉,以腾出新的空间接受新的数据。该策略主要比较缓存元素的创建时间。在一些对数据实效性要求比较高的场景下,可考虑选择该类策略,优先保障最新数据可用。

    最少使用策略--

    无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间。该策略主要比较元素的hitCount(命中次数),在保证高频数据有效性场景下,可选择这类策略。

    最近最少使用策略--

    无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素释放空间。该策略主要比较缓存最近一次被get使用时间。在热点数据场景下较适用,优先保证热点数据的有效性。

    随机淘汰策略--

    无论是否过期,随机淘汰某个缓存,如果对缓存数据没有任何要求,可以考虑使用该策略。

    不淘汰策略--

    当缓存达到指定值之后,不淘汰任何缓存,而是不能新增缓存,直到有缓存淘汰时,才能继续添加缓存。

     

    展开全文
  • Java简单实现本地缓存

    千次阅读 2019-04-21 08:04:58
    为了实现一致性hash算法(算法见https://blog.csdn.net/weixin_35971547/article/details/89427847),简单的编写了一个本地缓存的实现类,记录在此,供大家参考。 LocalCache.java import org.slf4j.Logger; ...
  • Java本地缓存

    千次阅读 2019-07-06 22:19:25
    文章目录Java内存缓存什么是缓存为什么要用缓存Java内存缓存Guava Cache Java内存缓存 什么是缓存 在计算中,缓存是一个高速数据存储层,其中存储了数据子集,且通常是短暂性存储,这样日后再次请求此数据时,...
  • 缓存(Cache) 是指将程序或系统中常用的数据对象存储在像内存这样特定的介质中,以避免在每次程序调用时,重新创建或...本地缓存是指程序级别的缓存组件,它的特点是本地缓存和应用程序会运行在同一个进程中,所以本地
  • 如何实现本地缓存和分布式缓存1)本地缓存和分布式缓存1.1)本地缓存1.1)分布式缓存2)知识扩展2.1)EhCache 和 Guava 的使用及特点分析2.2)LRU 和 LFU 的区别3)EhCache 基础使用4)手动实现一个缓存系统5)小结 ...
  • java本地缓存技术或框架

    千次阅读 2017-02-16 15:08:02
    java中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下。自己构造单例、guava、ehcache基本上涵盖了目前的大多数行为了。   为什么要有本地缓存? 在系统中,有些...
  • 前言 对一个java后台开发者而言,提到缓存,第一反应就是redis和memcache。利用这类缓存足以解决大多数的性能问题了,并且java针对这两者也都有非常...与分布式缓存对应的是本地缓存,缓存的进程和应用进程是同一个,
  • Java高性能本地缓存框架Caffeine

    千次阅读 2020-11-16 16:39:22
    文章目录Java高性能本地缓存框架Caffeine如何使用缓存加载手动加载自动加载手动异步加载自动异步加载过期策略基于大小基于时间基于引用Caffeine.weakKeys()Caffeine.weakValues()Caffeine.softValues()缓存移除...
  • java web 项目中对频繁读取且相对稳定的数据一般都是用了缓存...一个简易的本地缓存实现。 首先数据一般都是有时效性的,不是放入缓存就一直存在,如果超过一定时间没有被使用则应当被清空,使其系统中不会使用到...
  • java本地缓存的使用,方便快捷 代码: 1、新建一个Cache实体 package com.jdc.cache; public class Cache { private String key;//缓存ID private Object value;//缓存数据 private long timeOut;//更新...
  • Java自动过期本地缓存简单实现

    万次阅读 2018-04-12 10:59:20
    实际项目中常常需要用到本地缓存,特别是一些固定不变的数据,不想频繁调接口,因为http请求本身需要耗时,下面几个类对本地缓存作了简单实现,支持自动过期功能LocalCache.javainterface LocalCache { public ...
  • Java8 Caffeine 本地缓存

    千次阅读 2020-05-26 18:06:44
    一、本地缓存介绍 缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。 之前介绍过 Redis 这种 NoSql 作为缓存组件,它能够很好的...
  • Java8 构建本地缓存

    千次阅读 2016-10-10 14:50:43
    偶然想到了,之前一直用redis做key-value的缓存,为什么不能用HashMap用作缓存呢? 理由应该是redis中有很多封装好的功能,比如缓存持久化这种。缓存实现使用的ConcurrentHashMap做缓存,避免了递归的重复计算:...
  • java 通过文件实现缓存

    热门讨论 2013-09-27 13:59:25
    java实现缓存可以通过读取本地文件的方式实现,改代码就是通过读取本地文件实现缓存的简单例子
  • java对象本地缓存机制的实现

    千次阅读 2014-11-29 17:30:44
    本地缓存机制,利用java.util.concurrent,很好的管理本地内存存储的对象内容。 创建属性:  /**  * 具体内容存放的地方  */  private ConcurrentHashMap[] caches;  /**  * 超期信息存储  *...
  • 四:Java本地缓存标准 五:Java开源缓存框架 六:再次实现本地缓存 一:什么是缓存 所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例。这样做...
  • 关于java中的本地缓存-总结概

    千次阅读 2017-04-25 23:03:57
    为什么要有本地缓存? 在系统中,有些数据,数据量小,但是访问十分频繁(例如国家标准行政区域数据),针对这种场景,需要将数据搞到应用的本地缓存中,以提升系统的访问效率,减少无谓的数据库访问(数据库访问...
  • 您必须决定:什么时候不值得保留缓存条目?guava提供了三种基本类型的数据驱逐:基于缓存对象数量的驱逐、基于时间的驱逐和基于引用的驱逐。 基于缓存对象数量的驱逐 如果你的缓存对象数量不应该增长超过一定的...
  • 所以要将这些数据放在本地缓存中,并定时更新。 LoadingCache的使用过程中主要涉及两个类LoadingCache和CacheBuilder。 CacheBuilder使用来定义一些更新删除策略,最后生成LoadingCache对象。 CacheBuilder的主要...
  • java中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下。自己构造单例、guava、ehcache基本上涵盖了目前的大多数行为了。   为什么要有本地缓存? 在系统中,有些...
  • 这里我将会给大家演示用ConcurrentHashMap类和lambda表达式实现一个本地缓存。因为Map有一个新的方法,在key为Null的时候自动计算一个新的value值。非常适合实现cache。来看下代码: 当然,这种方式很傻瓜。即使...
  • 如何使用Java实现简单的本地缓存

    千次阅读 2017-06-26 15:30:42
    一般我们是通过key从缓存中读取value,如果读取不到则读取数据库,将数据库读取到的数据再放入缓存中。 首先数据一般都是有一定的时效性的,也就是说,不是放入缓存中就一直都会存在,如果超过一定时间没有被使用则...
  • 本地有一些数据需要用到map存储,希望能够设置过期时间,用远程缓存redis、memcache的话可以实现,但是又觉得大材小用,毕竟仅仅是少量的数据。 ExpiringMap 实现了这个功能 ExpiringMap github 最新maven依赖 <!...
  • java本地缓存

    千次阅读 2018-08-16 15:29:29
    这里本地缓存的含义是 多个线程公用的一个静态的Map对象 作用是减少db或cache的查询次数。 使用场景为静态或者非敏感数据。 也可以使用google的guava cache等   缓存 import lombok.AllArgsConstructor; ...
  • Caffeine 是一个基于Java 8的高性能本地缓存框架,其结构和 Guava Cache 基本一样,api也一样,基本上很容易就能替换。 Caffeine 实际上就是在 Guava Cache 的基础上,利用了一些 Java 8 的新特性,提高了某些场景下...
  • Caffeine Cache-高性能Java本地缓存组件

    千次阅读 2019-06-15 19:26:17
    前面刚说到Guava Cache...这一篇我们将要谈到一个新的本地缓存框架:Caffeine Cache。它也是站在巨人的肩膀上-Guava Cache,借着他的思想优化了算法发展而来。 本篇博文主要介绍Caffine Cache 的使用方式,以及Caffi...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 187,822
精华内容 75,128
关键字:

java建立本地缓存

java 订阅