精华内容
下载资源
问答
  • 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-02-15 17:13:04
     对于缓存大家应该都不陌生,缓存的核心是用空间换实践,通过分配一块高速存储区域(一般都是内存)来提高数据的读写效率,其实现的难点在于清空策略的实现,比较合理的思路就是定时回收与及时判断数据是否过期相...

    目录

    基础概念和问题

    缓存相关概念

    缓存相关问题

    本地缓存

    Guava Cache

    EHCache

    远程缓存

    Redis集群


     对于缓存大家应该都不陌生,缓存的核心是用空间换实践,通过分配一块高速存储区域(一般都是内存)来提高数据的读写效率,其实现的难点在于清空策略的实现,比较合理的思路就是定时回收与及时判断数据是否过期相结合。

       下面文章是转发的,主要从本地缓存、远程缓存和分布式缓存集群几个方面来介绍缓存的相关概念。仅供大家参考学习。

    基础概念和问题

    缓存相关概念

    1. 命中率:命中率指请求次数与正确返回结果次数的比例,其影响因素包括数据实时性,如果股票类实时性要求很高的数据,缓存的命中率会很低;缓存粒度问题, 如果KEY值包含的条件太多,会出现缓存命中率特别低的情况。通常来说,提高缓存命中率的方法包括增大缓存空间的大小的;对热点数据进行实时更新;调整缓存KEY的算法,保证缓存KEY的细粒度,如key-value;根据业务需要合理调整缓存的过期策略。
    2. 最大元素:缓存中可以存放的元素的最大数量。
    3. 清空策略包括FIFO,最先进入缓存的数据在空间不够时会被优先清理;LFU一直以来最少被使用的元素会被清理,可以给缓存元素设置一个计数器实现;LRU最近最少使用的缓存元素会被清理,可以通过一个时间戳来将最近未使用数据清除。
    4. 预热策略包括全量预热,一开始就加载全部数据,适用于不怎么变化的数据(地区数据);增量预热,查询不到时从数据源取出放入缓存。

    缓存相关问题

    1. 缓存穿透:一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。解决方法包括将查询结果为空的情况也进行缓存,缓存时间设置短一点,并在该key对应的数据insert之后清理缓存;对一定不存在的key进行过滤。
    2. 缓存雪崩缓存服务器重启或者大量缓存集中在某一个时间段失效,这时会给后端系统(比如DB)带来很大压力。解决方案包括在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,比如对某个key只允许一个线程查询数据和写缓存,其他线程等待;不同的key设置不同的过期时间,让缓存失效的时间点尽量均匀;做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

    本地缓存

    Java的本地缓存很早就有了相关标准javax.cache,要求的特性包括原子操作、缓存读写、缓存事件监听器、数据统计等内容。实际工作中本地缓存主要用于特别频繁的稳定数据,不然的话带来的数据不一致会得不偿失。实践中,常使用Guava Cache,以及与Spring结合良好的EhCache

    Guava Cache

         这是一个全内存的本地缓存实现,它提供了线程安全的实现机制,简单易用,性能好。其创建方式包括cacheLoadercallable callback两种,前者针对整个cache,而后者比较灵活可以在get时指定。 CacheBuilder.newBuilder()方法创建cache时重要的几个方法如下所示,之后是一个简单的使用示例。 maximumSize(long):设置容量大小,超过就开始回收。 expireAfterAccess(long, TimeUnit):在这个时间段内没有被读/写访问,就会被回收。 expireAfterWrite(long, TimeUnit):在这个时间段内没有被写访问,就会被回收 。 removalListener(RemovalListener):监听事件,在元素被删除时,进行监听。

    @Service
    public class ConfigCenterServiceImpl implements ConfigCenterService {
        private final static long maximumSize = 20;
        /**
         * 最大20个,过期时间为1天
         */
        private Cache<String, Map<String, ConfigAppSettingDto>> cache = CacheBuilder.newBuilder().maximumSize(maximumSize)
                .expireAfterWrite(1, TimeUnit.DAYS).build();
        @Autowired
        private ConfigAppSettingDAO configAppSettingDAO;
    
        @Override
        public ConfigAppSettingDto getByTypeNameAndKey(String configType, String appID, String key) {
            Map<String, ConfigAppSettingDto> map = getByType(configType, appID);
            return map.get(key);
        }
    
        /************************** 辅助方法 ******************************/
        private Map<String, ConfigAppSettingDto> getByType(String configType, String appID) {
            try {
                return cache.get(configType, new Callable<Map<String, ConfigAppSettingDto>>() {
                    @Override
                    public Map<String, ConfigAppSettingDto> call() throws Exception {
                        Map<String, ConfigAppSettingDto> result = Maps.newConcurrentMap();
                        List<ConfigAppSetting> list = configAppSettingDAO.getByTypeName(configType, appID);
                        if (null != list && !list.isEmpty()) {
                            for (ConfigAppSetting item : list) {
                                result.put(item.getAppkey(), new ConfigAppSettingDto(item.getAppkey(), item.getAppvalue(),
                                        item.getDescription()));
                            }
                        }
                        return result;
                    }
                });
            } catch (ExecutionException ex) {
                throw new BizException(300, "获取ConfigAppSetting配置信息失败");
            }
        }
    }

    EHCache

           EHCache也是一个全内存的本地缓存实现,符合javax.cache JSR-107规范,被应用在Hibernate中,过去存在过期失效的缓存元素无法被GC掉,造成内存泄露的问题,其主要类型及使用示例如下所示。 Element:缓存的元素,它维护着一个键值对。 Cache:它是Ehcache的核心类,它有多个Element,并被CacheManager管理,实现了对缓存的逻辑操作行为。 CacheManager:Cache的容器对象,并管理着Cache的生命周期。

    Spring Boot整合Ehcache示例

    //maven配置
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
    </dependency>
    
    
    
    
    //开启Cache
    @SpringBootApplication
    @EnableCaching
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
    
    
    //方式1
    @CacheConfig(cacheNames = "users")
    public interface UserRepository extends JpaRepository<User, Long> {
        @Cacheable
        User findByName(String name);
    }
    
    
    
    //方式2
    @Service
    public class CacheUserServiceImpl implements CacheUserService {
        @Autowired
        private UserMapper userMapper;
        @Override
        public List<User> getUsers() {
            return userMapper.findAll();
        }
        // Cacheable表示获取缓存,内容会存储在people中,包含两个Key-Value
        @Override
        @Cacheable(value = "people", key = "#name")
        public User getUser(String name) {
            return userMapper.findUserByName(name);
        }
        //put是存储
        @CachePut(value = "people", key = "#user.userid")
        public User save(User user) {
            User finalUser = userMapper.insert(user);
            return finalUser;
        }
        //Evict是删除
        @CacheEvict(value = "people")
        public void remove(Long id) {
            userMapper.delete(id);
        }
    }
    
    
    
    //在application.properties指定spring.cache.type=ehcache即可
    //在src/main/resources中创建ehcache.xml
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:noNamespaceSchemaLocation="ehcache.xsd">
        <cache name="users"
               maxEntriesLocalHeap="100"
               timeToLiveSeconds="1200">
        </cache>
    </ehcache>

     

    远程缓存

          常见的远程缓存组件包括memcached,redis等。前者性能高效,使用方便,但功能相对单一,只支持字符串类型的数据,需要结合序列化协议,只能用作缓存。后者是目前最流行的缓存服务器,具有高效的存取速度,高并发的吞吐量,并且有丰富的数据类型,支持持久化。因此,应用场景非常多,包括数据缓存、分布式队列、分布式锁、消息中间件等。

         Redis支持更丰富的数据结构, 例如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 此外,Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。可以说Redis兼具了缓存系统和数据库的一些特性,因此有着丰富的应用场景。本文介绍Redis在Spring Boot中两个典型的应用场景。 场景1:数据缓存

    //maven配置
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-redis</artifactId>  
    </dependency>
    
    //application.properties配置
    # Redis数据库索引(默认为0)
    spring.redis.database=0  
    # Redis服务器地址
    spring.redis.host=localhost
    # Redis服务器连接端口
    spring.redis.port=6379  
    # Redis服务器连接密码(默认为空)
    spring.redis.password=  
    # 连接池最大连接数
    spring.redis.pool.max-active=8  
    # 连接池最大阻塞等待时间(使用负值表示没有限制)
    spring.redis.pool.max-wait=-1  
    # 连接池中的最大空闲连接
    spring.redis.pool.max-idle=8  
    # 连接池中的最小空闲连接
    spring.redis.pool.min-idle=0
    
    //方法1
    @Configuration
    @EnableCaching
    public class CacheConfig {
        @Autowired
        private JedisConnectionFactory jedisConnectionFactory;
        @Bean
        public RedisCacheManager cacheManager() {
            RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
            return redisCacheManager;
        }
        @Bean
        public RedisTemplate<Object, Object> redisTemplate() {
            RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
            redisTemplate.setConnectionFactory(jedisConnectionFactory);
            // 开启事务支持
            redisTemplate.setEnableTransactionSupport(true);
            // 使用String格式序列化缓存键
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            redisTemplate.setKeySerializer(stringRedisSerializer);
            redisTemplate.setHashKeySerializer(stringRedisSerializer);
            return redisTemplate;
        }
    }
    
    //方法2,和之前Ehcache方式一致

    场景2:共享Session

    //maven配置
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    //Session配置
    @Configuration
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*)//
    public class SessionConfig {
    }
    //示例
    @RequestMapping("/session")
    @RestController
    public class SessionController {
        @Autowired
        private UserRepository userRepository;
    
        @RequestMapping("/user/{id}")
        public User getUser(@PathVariable Long id, HttpSession session) {
            User user = (User) session.getAttribute("user" + id);
            if (null == user) {
                user = userRepository.findOne(id);
                session.setAttribute("user" + id, user);
            }
            return user;
        }
    }

      Spring Redis默认使用JDK进行序列化和反序列化,因此被缓存对象需要实现java.io.Serializable接口,否则缓存出错。

     

    Redis集群

    实现包括如下3种方式,相对于传统的大客户端分片和代理模式,路由查询的方式比较新颖,具体解决方案推荐redis-cluster

    • 客户端分片:包括jedis在内的一些客户端,都实现了客户端分片机制。
    • 基于代理的分片:Twemproxy、codis,客户端发送请求到一个代理,代理解析客户端的数据,将请求转发至正确的节点,然后将结果回复给客户端。
    • 路由查询:Redis-cluster,将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。

      redis集群具体的实现示例可以参考原文。还没有完全都理解透彻,就不在这里摘录了。

     

      文章来源:Java缓存深入理解

     

     

     

     

     

     

     

     

    展开全文
  • Java实现LRU缓存

    千次阅读 2018-03-14 13:27:43
    原文地址:https://www.cnblogs.com/lzrabbit/p/3734850.htmlLRU缓存实现(Java) LRU Cache的LinkedHashMap实现LRU Cache的链表+HashMap实现LinkedHashMap的FIFO实现调用示例LRU是Least Recently Used 的缩写,翻译...

    原文地址:https://www.cnblogs.com/lzrabbit/p/3734850.html

    LRU缓存实现(Java)

    LRU是Least Recently Used 的缩写,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用LRU算法实现的话就是将最老的数据删掉,废话不多说,下面来说下Java版的LRU缓存实现

    Java里面实现LRU缓存通常有两种选择,一种是使用LinkedHashMap,一种是自己设计数据结构,使用链表+HashMap

    LRU Cache的LinkedHashMap实现

    LinkedHashMap自身已经实现了顺序存储,默认情况下是按照元素的添加顺序存储,也可以启用按照访问顺序存储,即最近读取的数据放在最前面,最早读取的数据放在最后面,然后它还有一个判断是否删除最老数据的方法,默认是返回false,即不删除数据,我们使用LinkedHashMap实现LRU缓存的方法就是对LinkedHashMap实现简单的扩展,扩展方式有两种,一种是inheritance,一种是delegation,具体使用什么方式看个人喜好

    复制代码
    //LinkedHashMap的一个构造函数,当参数accessOrder为true时,即会按照访问顺序排序,最近访问的放在最前,最早访问的放在后面
    public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
            super(initialCapacity, loadFactor);
            this.accessOrder = accessOrder;
    }
    
    //LinkedHashMap自带的判断是否删除最老的元素方法,默认返回false,即不删除老数据
    //我们要做的就是重写这个方法,当满足一定条件时删除老数据
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
            return false;
    }
    复制代码

    LRU缓存LinkedHashMap(inheritance)实现

    采用inheritance方式实现比较简单,而且实现了Map接口,在多线程环境使用时可以使用 Collections.synchronizedMap()方法实现线程安全操作

    复制代码
    package cn.lzrabbit.structure.lru;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Created by liuzhao on 14-5-15.
     */
    public class LRUCache2<K, V> extends LinkedHashMap<K, V> {
        private final int MAX_CACHE_SIZE;
    
        public LRUCache2(int cacheSize) {
            super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
            MAX_CACHE_SIZE = cacheSize;
        }
    
        @Override
        protected boolean removeEldestEntry(Map.Entry eldest) {
            return size() > MAX_CACHE_SIZE;
        }
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<K, V> entry : entrySet()) {
                sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));
            }
            return sb.toString();
        }
    }
    复制代码

     这样算是比较标准的实现吧,实际使用中这样写还是有些繁琐,更实用的方法时像下面这样写,省去了单独见一个类的麻烦

    复制代码
    final int cacheSize = 100;
    Map<String, String> map = new LinkedHashMap<String, String>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
        return size() > cacheSize;
        }
    };
    复制代码

     

    LRU缓存LinkedHashMap(delegation)实现

    delegation方式实现更加优雅一些,但是由于没有实现Map接口,所以线程同步就需要自己搞定了

    复制代码
    package cn.lzrabbit.structure.lru;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * Created by liuzhao on 14-5-13.
     */
    public class LRUCache3<K, V> {
    
        private final int MAX_CACHE_SIZE;
        private final float DEFAULT_LOAD_FACTOR = 0.75f;
        LinkedHashMap<K, V> map;
    
        public LRUCache3(int cacheSize) {
            MAX_CACHE_SIZE = cacheSize;
            //根据cacheSize和加载因子计算hashmap的capactiy,+1确保当达到cacheSize上限时不会触发hashmap的扩容,
            int capacity = (int) Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTOR) + 1;
            map = new LinkedHashMap(capacity, DEFAULT_LOAD_FACTOR, true) {
                @Override
                protected boolean removeEldestEntry(Map.Entry eldest) {
                    return size() > MAX_CACHE_SIZE;
                }
            };
        }
    
        public synchronized void put(K key, V value) {
            map.put(key, value);
        }
    
        public synchronized V get(K key) {
            return map.get(key);
        }
    
        public synchronized void remove(K key) {
            map.remove(key);
        }
    
        public synchronized Set<Map.Entry<K, V>> getAll() {
            return map.entrySet();
        }
    
        public synchronized int size() {
            return map.size();
        }
    
        public synchronized void clear() {
            map.clear();
        }
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            for (Map.Entry entry : map.entrySet()) {
                sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));
            }
            return sb.toString();
        }
    }
    复制代码

     LRU Cache的链表+HashMap实现

     注:此实现为非线程安全,若在多线程环境下使用需要在相关方法上添加synchronized以实现线程安全操作

    复制代码
    package cn.lzrabbit.structure.lru;
    
    
    import java.util.HashMap;
    
    /**
     * Created by liuzhao on 14-5-12.
     */
    public class LRUCache1<K, V> {
    
        private final int MAX_CACHE_SIZE;
        private Entry first;
        private Entry last;
    
        private HashMap<K, Entry<K, V>> hashMap;
    
        public LRUCache1(int cacheSize) {
            MAX_CACHE_SIZE = cacheSize;
            hashMap = new HashMap<K, Entry<K, V>>();
        }
    
        public void put(K key, V value) {
            Entry entry = getEntry(key);
            if (entry == null) {
                if (hashMap.size() >= MAX_CACHE_SIZE) {
                    hashMap.remove(last.key);
                    removeLast();
                }
                entry = new Entry();
                entry.key = key;
            }
            entry.value = value;
            moveToFirst(entry);
            hashMap.put(key, entry);
        }
    
        public V get(K key) {
            Entry<K, V> entry = getEntry(key);
            if (entry == null) return null;
            moveToFirst(entry);
            return entry.value;
        }
    
        public void remove(K key) {
            Entry entry = getEntry(key);
            if (entry != null) {
                if (entry.pre != null) entry.pre.next = entry.next;
                if (entry.next != null) entry.next.pre = entry.pre;
                if (entry == first) first = entry.next;
                if (entry == last) last = entry.pre;
            }
            hashMap.remove(key);
        }
    
        private void moveToFirst(Entry entry) {
            if (entry == first) return;
            if (entry.pre != null) entry.pre.next = entry.next;
            if (entry.next != null) entry.next.pre = entry.pre;
            if (entry == last) last = last.pre;
    
            if (first == null || last == null) {
                first = last = entry;
                return;
            }
    
            entry.next = first;
            first.pre = entry;
            first = entry;
            entry.pre = null;
        }
    
        private void removeLast() {
            if (last != null) {
                last = last.pre;
                if (last == null) first = null;
                else last.next = null;
            }
        }
    
    
        private Entry<K, V> getEntry(K key) {
            return hashMap.get(key);
        }
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            Entry entry = first;
            while (entry != null) {
                sb.append(String.format("%s:%s ", entry.key, entry.value));
                entry = entry.next;
            }
            return sb.toString();
        }
    
        class Entry<K, V> {
            public Entry pre;
            public Entry next;
            public K key;
            public V value;
        }
    }
    复制代码

    LinkedHashMap的FIFO实现

    FIFO是First Input First Output的缩写,也就是常说的先入先出,默认情况下LinkedHashMap就是按照添加顺序保存,我们只需重写下removeEldestEntry方法即可轻松实现一个FIFO缓存,简化版的实现代码如下

    复制代码
    final int cacheSize = 5;
    LinkedHashMap<Integer, String> lru = new LinkedHashMap<Integer, String>() {
        @Override
        protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) {
        return size() > cacheSize;
        }
    };
    复制代码

    调用示例

    测试代码

    复制代码
    package cn.lzrabbit.structure.lru;
    
    import cn.lzrabbit.ITest;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Created by liuzhao on 14-5-15.
     */
    public class LRUCacheTest  {
    
        public static void main(String[] args) throws Exception {
            System.out.println("start...");
    
            lruCache1();
            lruCache2();
            lruCache3();
            lruCache4();
         
            System.out.println("over...");
        }
     
    
     static   void lruCache1() {
            System.out.println();
            System.out.println("===========================LRU 链表实现===========================");
            LRUCache1<Integer, String> lru = new LRUCache1(5);
            lru.put(1, "11");
            lru.put(2, "11");
            lru.put(3, "11");
            lru.put(4, "11");
            lru.put(5, "11");
            System.out.println(lru.toString());
            lru.put(6, "66");
            lru.get(2);
            lru.put(7, "77");
            lru.get(4);
            System.out.println(lru.toString());
            System.out.println();
        }
    
    
    static   <T> void lruCache2() {
            System.out.println();
            System.out.println("===========================LRU LinkedHashMap(inheritance)实现===========================");
            LRUCache2<Integer, String> lru = new LRUCache2(5);
            lru.put(1, "11");
            lru.put(2, "11");
            lru.put(3, "11");
            lru.put(4, "11");
            lru.put(5, "11");
            System.out.println(lru.toString());
            lru.put(6, "66");
            lru.get(2);
            lru.put(7, "77");
            lru.get(4);
            System.out.println(lru.toString());
            System.out.println();
        }
    
      static  void lruCache3() {
            System.out.println();
            System.out.println("===========================LRU LinkedHashMap(delegation)实现===========================");
            LRUCache3<Integer, String> lru = new LRUCache3(5);
            lru.put(1, "11");
            lru.put(2, "11");
            lru.put(3, "11");
            lru.put(4, "11");
            lru.put(5, "11");
            System.out.println(lru.toString());
            lru.put(6, "66");
            lru.get(2);
            lru.put(7, "77");
            lru.get(4);
            System.out.println(lru.toString());
            System.out.println();
        }
    
      static  void lruCache4() {
            System.out.println();
            System.out.println("===========================FIFO LinkedHashMap默认实现===========================");
            final int cacheSize = 5;
            LinkedHashMap<Integer, String> lru = new LinkedHashMap<Integer, String>() {
                @Override
                protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) {
                    return size() > cacheSize;
                }
            };
            lru.put(1, "11");
            lru.put(2, "11");
            lru.put(3, "11");
            lru.put(4, "11");
            lru.put(5, "11");
            System.out.println(lru.toString());
            lru.put(6, "66");
            lru.get(2);
            lru.put(7, "77");
            lru.get(4);
            System.out.println(lru.toString());
            System.out.println();
        }
    
    }
    复制代码
    View Code

    运行结果

    复制代码
    "C:\Program Files (x86)\Java\jdk1.6.0_10\bin\java" -Didea.launcher.port=7535 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA 13.0.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\charsets.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\deploy.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\javaws.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\jce.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\jsse.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\management-agent.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\plugin.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\resources.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\rt.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\dnsns.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\localedata.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\sunjce_provider.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\sunmscapi.jar;C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\ext\sunpkcs11.jar;D:\SVN\projects\Java\Java.Algorithm\target\test-classes;D:\SVN\projects\Java\Java.Algorithm\target\classes;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 13.0.2\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain Main
    start...
    
    ===========================LRU 链表实现===========================
    5:11 4:11 3:11 2:11 1:11 
    4:11 7:77 2:11 6:66 5:11 
    
    
    ===========================LRU LinkedHashMap(inheritance)实现===========================
    1:11 2:11 3:11 4:11 5:11 
    5:11 6:66 2:11 7:77 4:11 
    
    
    ===========================LRU LinkedHashMap(delegation)实现===========================
    1:11 2:11 3:11 4:11 5:11 
    5:11 6:66 2:11 7:77 4:11 
    
    
    ===========================FIFO LinkedHashMap默认实现===========================
    {1=11, 2=11, 3=11, 4=11, 5=11}
    {3=11, 4=11, 5=11, 6=66, 7=77}
    
    over...
    
    Process finished with exit code 0
    复制代码

    展开全文
  • java实现缓存

    2013-09-27 10:51:15
    主要实现的是针对大量数据的内存缓存,也就是实现一个类中静态Map,对这个Map进行常规的增删查失效等。
  • java实现本地缓存

    千次阅读 2016-12-12 10:44:47
    使用场景为静态或者非敏感数据。 可以使用google的guava cache。 本文尝试自己写本地缓存。 import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import java.util.HashMap; import ...

    这里本地缓存的含义是 多个线程公用的一个静态的Map对象

    作用是减少db或cache的查询次数。

    使用场景为静态或者非敏感数据。

    也可以使用google的guava cache等


    缓存类

    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.Setter;
    
    import java.util.HashMap;
    import java.util.Map;
    
    
    public class LocalCache {
    
        //缓存Map
        private static Map<String,CacheContent> map = new HashMap<>();
        private static  LocalCache localCache = new LocalCache();
    
        private LocalCache(){
        }
    
        public  String getLocalCache(String key) {
            CacheContent cc = map.get(key);
    
            if(null == cc) {
                return null;
            }
    
            long currentTime = System.currentTimeMillis();
    
            if(cc.getCacheMillis() > 0 && currentTime - cc.getCreateTime() > cc.getCacheMillis()) {
                //超过缓存过期时间,返回null
                map.remove(key);
                return null;
            } else {
                return cc.getElement();
            }
        }
    
        public void setLocalCache(String key,int cacheMillis,String value) {
            long currentTime = System.currentTimeMillis();
            CacheContent cc = new CacheContent(cacheMillis,value,currentTime);
            map.put(key,cc);
        }
    
        public static LocalCache getInStance(){
            return localCache;
        }
    
        @Getter
        @Setter
        @AllArgsConstructor
        class CacheContent{
            // 缓存生效时间
            private  int cacheMillis;
            // 缓存对象
            private String element;
            // 缓存创建时间
            private long createTime ;
    
        }
    
    
    
    }
    

    调用代码

    	//先查询本地缓存
                    String key ="testkey";
    		LocalCache localCache = LocalCache.getInStance();
    		String value = localCache.getLocalCache(key);
    
    		if(StringUtils.isBlank(value)) {
                            //从db或cache获取数据
    			value = RedisClient.get(key);
    			//设置本地缓存,生效时间为10秒
    			localCache.setLocalCache(key ,10000,value);
    		}





    展开全文
  • Java实现多种缓存机制

    万次阅读 多人点赞 2017-03-31 15:46:46
    在学习的过程中,遇到用java写一个LRU缓存。鉴于此,把所有缓存机制的缓存都整理了一下,并自己写了几套,主要分为FIFO,LRU和LFU三种,在技术点中会解释这三种是什么意思。大家一起研究和优化。笔者目前整理的一些...
  • Java手动实现缓存

    千次阅读 2017-07-06 10:21:27
    今天刚学完线程的读写锁,发现使用锁能够使得缓存数据更安全,于是便自己手动实现了一下。缓存的基本原理是当用户取数据时,先判断缓存里面是否有需要的值,有则直接返回,节省了对数据库的操作。下面是自己的一个...
  • JAVA实现分布式缓存

    千次阅读 2009-11-25 08:43:00
    1概述 通常情况下我们运行程序的过程中会产生一些中间数据,这些中间数据需要在将来的某个时间... 实际上分布式缓存根本上就是提供一个附加内存让另一台机器帮忙存储和查找数据。2实现方法 首先建立一个集合对象,
  • JAVA实现HASHMAP缓存 并定时清理

    千次阅读 2019-04-02 09:56:48
    总体思路:使用ConcurrentHashMap去存缓存的对象,PriorityBlockingQueue去优化弹出(定时移除,按失效时间排序),ScheduledExecutorService去定期执行移除,初始化时调用单例对象的initial()方法 import java....
  • 主要介绍了java使用hashMap缓存保存数据的方法,结合实例形式简单分析了java基于hashmap读写缓存数据的相关操作技巧,需要的朋友可以参考下
  • Java 实现本地缓存工具

    千次阅读 2016-12-01 12:11:40
    基本思路: 1、继承线程安全的ConcurrentHashMap  ...经测试:存储100万以内普通数据的频繁读写性能还可以,能满足中小型系统缓存需求,如果要多机器共享缓存,建议单独部署一个缓存服务器 /**  * @Title: Lo
  • 分页缓存<用java实现分页,并读取过程先读取缓存数据>
  • 基于Java使用HashMap实现数据缓存

    千次阅读 2018-03-05 09:44:12
    1.最近任务需要将数据进行不断比较并输出数据,数据是多条,利用HashMap和List结合实现数据缓存。 代码如下: public class MapTest { private static HashMap&lt;String,List&lt;String&gt;&gt;...
  • 在项目的开发过程中,经常会考虑到提高用户访问效率,降低服务器的压力,这个时候会用到数据缓存。当前实现缓存的技术有很多,例如: jCache、Ehcache以及缓存 服务器redis,redis作为分布式系统重要的组成部分,该...
  • Java实现简单LRU缓存机制

    千次阅读 2020-05-25 11:24:51
    Java实现简单LRU缓存机制 一、什么是 LRU 算法 就是一种缓存淘汰策略。 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置。但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而...
  • java 通过文件实现缓存

    热门讨论 2013-09-27 13:59:25
    java实现缓存可以通过读取本地文件的方式实现,改代码就是通过读取本地文件实现缓存的简单例子
  • redis分布式锁java实现解决缓存雪崩

    千次阅读 2017-10-26 17:32:43
    因此,通常解决方案往往是使用互斥锁,让一个线程访问数据库,并将数据更新到缓存中,其他线程访问缓存数据。如果是基于jvm锁机制的话,只能解决单机问题,也就是只让本机一个线程访问缓存,但是分布式条件下
  • 主要介绍了Java中常用缓存Cache机制的实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • java中,数据缓存实现

    万次阅读 2016-07-12 10:47:32
    首先 我们 先写两个类,一个是Cache(缓存类)和另外一个CacheManager(缓存管理类) 1 Cache类 package com.yhsx.service.impl; /** * Created by dhb on 2016/6/24. */ public class Cache { private ...
  • spring整合redis实现数据缓存

    万次阅读 2018-09-16 14:12:14
    本文利用spring整合redis将查询数据存入redis实现数据缓存。 数据缓存原因:有些数据比较多,如果每次访问都要进行查询,无疑给数据库带来太大的负担,将一些庞大的查询数据并且更新次数较少的数据存入redis,能为...
  • Java 内存缓存数据(map实现

    千次阅读 2019-03-21 11:25:44
    方式一:单一容器存储 import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; ... * @Title: WeatherCacheUtils ... * @Description: 常用数据缓存工具 * @a...
  • java实现缓存框架

    千次阅读 2018-07-08 23:06:14
    缓存的原理外存: 外储存器是指除计算机内存及CPU缓存以外的储存器,此类储存器一般断电后仍然能保存数据。常见的外存储器有硬盘、软盘、光盘、U盘等,一般的软件都是安装在外存中(windows系统指的是CDEF盘, Linux...
  • java本地缓存实现

    千次阅读 2019-04-16 21:42:15
    java本地缓存实现
  • 本篇文章主要介绍了Java本地缓存实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 1.最近任务需要将数据进行不断比较并输出数据,数据是多条,利用HashMap和List结合实现数据缓存。 代码如下: public class MapTest { public class MapTest { private static HashMapList> mapSave = new ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 492,458
精华内容 196,983
关键字:

java实现数据缓存

java 订阅