当前位置: 首页 > news >正文

LocalCache

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

引言

一般来说,应用中的数据主要来自于数据库和缓存,现有的缓存中间件已经能满足大多数场景,但是有些场景更适合做服务器本地缓存,这就需要自己去实现了,这里给出了方攀的一个LocalCache的例子,并已在应用中得到了很好的应用。

关键词介绍

1、SoftReference 

只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong Ref时才回收对象,结合超时时间可以用来实现缓存。

2、ReferenceQueue

使得对象即使被GC了也可以持有ref,结合HashMap可以缩小键-值映射关系的数目

实现原理

1、service类

public class CacheManager {

    public interface ValueGenerator {

        Object generate();
    }

    private final CacheUtil cache;
    private final Lock      lock = new ReentrantLock();

    public CacheManager(int cacheSize, int expriedTime){
        cache = new CacheUtil(true, cacheSize, expriedTime);
    }

    /**
     * <pre>
     *     返回 key 对应的 value,如果 value 已经超时,
     *     则使用 ValueGenerator 产生新的 value,并返回。
     *     使用此方法,可以避免多线程同时产生新 value 的问题。
     * </pre>
     *
     * @param key
     * @param generator
     * @return
     */
    public Object get(String key, ValueGenerator generator) {
        Object result = cache.get(key);
        if (result != null) {
            return result;
        }
        try {
            lock.lock();
            result = cache.get(key);
            if (result != null) {
                return result;
            }
            Object value = generator.generate();
            cache.put(key, value);
            return value;
        } finally {
            lock.unlock();
        }
    }
}

2、cacheUtil

public class CacheUtil {

    private final static float            LOAD_FACTOR = 0.75f;

    private final Map<Object, CacheEntry> cacheMap;
    private int                           maxSize;
    private long                          lifetime;
    private final ReferenceQueue<Object>  queue;

    private final Lock                    lock        = new ReentrantLock();

    public CacheUtil(boolean soft, int maxSize){
        this(soft, maxSize, 0);
    }

    public CacheUtil(boolean soft, int maxSize, int lifetime){
        this.maxSize = maxSize;
        this.lifetime = lifetime * 1000;
        this.queue = soft ? new ReferenceQueue<Object>() : null;
        int buckets = (int) (maxSize / LOAD_FACTOR) + 1;
        cacheMap = new HashMap<Object, CacheEntry>(buckets, LOAD_FACTOR);
    }

    private void emptyQueue() {
        if (queue == null) {
            return;
        }
        while (true) {
            CacheEntry entry = (CacheEntry) queue.poll();
            if (entry == null) {
                break;
            }
            Object key = entry.getKey();
            if (key == null) {
                continue;
            }
            CacheEntry currentEntry = cacheMap.remove(key);
            if ((currentEntry != null) && (entry != currentEntry)) {
                cacheMap.put(key, currentEntry);
            }
        }
    }

    private void expungeExpiredEntries() {
        emptyQueue();
        if (lifetime == 0) {
            return;
        }
        int cnt = 0;
        long time = System.currentTimeMillis();
        for (Iterator<CacheEntry> t = cacheMap.values().iterator(); t.hasNext();) {
            CacheEntry entry = t.next();
            if (entry.isValid(time) == false) {
                t.remove();
                cnt++;
            }
        }
    }

    public int size() {
        try {
            lock.lock();
            expungeExpiredEntries();
            return cacheMap.size();
        } finally {
            lock.unlock();
        }
    }

    public void clear() {
        try {
            lock.lock();
            if (queue != null) {
                for (CacheEntry entry : cacheMap.values()) {
                    entry.invalidate();
                }
                while (queue.poll() != null) {
                }
            }
            cacheMap.clear();
        } finally {
            lock.unlock();
        }
    }

    public void put(Object key, Object value) {
        try {
            lock.lock();
            emptyQueue();
            long expirationTime = (lifetime == 0) ? 0 : System.currentTimeMillis() + lifetime;
            CacheEntry newEntry = newEntry(key, value, expirationTime, queue);
            CacheEntry oldEntry = cacheMap.put(key, newEntry);
            if (oldEntry != null) {
                oldEntry.invalidate();
                return;
            }
            if (maxSize > 0 && cacheMap.size() > maxSize) {
                expungeExpiredEntries();
                if (cacheMap.size() > maxSize) {
                    Iterator<CacheEntry> t = cacheMap.values().iterator();
                    CacheEntry lruEntry = t.next();
                    t.remove();
                    lruEntry.invalidate();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public Object get(Object key) {
        try {
            lock.lock();
            emptyQueue();
            CacheEntry entry = cacheMap.get(key);
            if (entry == null) {
                return null;
            }
            long time = (lifetime == 0) ? 0 : System.currentTimeMillis();
            if (entry.isValid(time) == false) {
                cacheMap.remove(key);
                return null;
            }
            return entry.getValue();
        } finally {
            lock.unlock();
        }
    }

    public void remove(Object key) {
        try {
            lock.lock();
            emptyQueue();
            CacheEntry entry = cacheMap.remove(key);
            if (entry != null) {
                entry.invalidate();
            }
        } finally {
            lock.unlock();
        }
    }

    public void setCapacity(int size) {
        try {
            lock.lock();
            expungeExpiredEntries();
            if (size > 0 && cacheMap.size() > size) {
                Iterator<CacheEntry> t = cacheMap.values().iterator();
                for (int i = cacheMap.size() - size; i > 0; i--) {
                    CacheEntry lruEntry = t.next();
                    t.remove();
                    lruEntry.invalidate();
                }
            }

            maxSize = size > 0 ? size : 0;
        } finally {
            lock.unlock();
        }
    }

    public void setTimeout(int timeout) {
        try {
            lock.lock();
            emptyQueue();
            lifetime = timeout > 0 ? timeout * 1000L : 0L;
        } finally {
            lock.unlock();
        }
    }

    protected CacheEntry newEntry(Object key, Object value, long expirationTime, ReferenceQueue<Object> queue) {
        if (queue != null) {
            return new SoftCacheEntry(key, value, expirationTime, queue);
        } else {
            return new HardCacheEntry(key, value, expirationTime);
        }
    }

3、cacheEntry(cacheUtil的内部类

private static interface CacheEntry {

        boolean isValid(long currentTime);

        void invalidate();

        Object getKey();

        Object getValue();

    }

private static class SoftCacheEntry extends SoftReference<Object> implements CacheEntry {

        private Object key;
        private long   expirationTime;

        SoftCacheEntry(Object key, Object value, long expirationTime, ReferenceQueue<Object> queue){
            super(value, queue);
            this.key = key;
            this.expirationTime = expirationTime;
        }

        public Object getKey() {
            return key;
        }

        public Object getValue() {
            return get();
        }

        public boolean isValid(long currentTime) {
            boolean valid = (currentTime <= expirationTime) && (get() != null);
            if (valid == false) {
                invalidate();
            }
            return valid;
        }

        public void invalidate() {
            clear();
            key = null;
            expirationTime = -1;
        }
    }

应用接入

/** 初始化内存cache大小为256,过期时间为300即300秒 */
    private static final CacheManager cacheManager                  = new CacheManager(256, 300);

public Map<Integer, DisplayArea<Course>> getCourseDisplayArea(final boolean withDetail, final int floorId) {
        /* 注意key唯一性,需要加上withDetail */
        return (Map<Integer, DisplayArea<Course>>) cacheManager.get(COURSE_CACHE_KEY_BY_FLOOR + floorId + withDetail,
                                                                    new ValueGenerator() {

                                                                        @Override
                                                                        public Object generate() {
                                                                            List<Integer> areaIds = DisplayAreaFeature.getCourseDisplayAreas(floorId);
                                                                            Map<Integer, DisplayArea<Course>> displayAreas = getCourseDisplayArea(withDetail,
                                                                                                                                                  areaIds);
                                                                            // 解析扩展属性 并填充标签
                                                                            return fillTags(displayAreas,
                                                                                            parseExtProperty(displayAreas));
                                                                        }

                                                                    });
    }

转载于:https://my.oschina.net/tryUcatchUfinallyU/blog/164397

相关文章:

  • 03_oracle 10g表空间创建步骤
  • LDA-线性判别分析(三)
  • 04_oracle锁表解锁语句
  • 【转载】ASP.NET MVC的过滤器【Filters】
  • 05_oracle 查看表空间的大小及使用情况sql语句
  • 每日英语:Tech Firms Flock to Vietnam
  • 06_SPOOL导出_表头模式
  • jetty8的多实例部署(LT项目开发参考)
  • 07_oracle正则表达式语法
  • 测试比json更快更小的二进制数据传输格式Msgpack [pythono MessagePack 版本]
  • 01_excel基础知识1
  • 学习Trie树,处理“海量”数据
  • hibernate的native sql查询
  • 类的成员变量和属性Fields and Properties in class
  • 一个简单的JavaScript Map
  • php的引用
  • [译]CSS 居中(Center)方法大合集
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • IDEA 插件开发入门教程
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • Javascript设计模式学习之Observer(观察者)模式
  • Material Design
  • Service Worker
  • Spring声明式事务管理之一:五大属性分析
  • vuex 学习笔记 01
  • 多线程 start 和 run 方法到底有什么区别?
  • 排序(1):冒泡排序
  • 我与Jetbrains的这些年
  • 项目实战-Api的解决方案
  • #pragma data_seg 共享数据区(转)
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • (WSI分类)WSI分类文献小综述 2024
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (四)linux文件内容查看
  • (一)u-boot-nand.bin的下载
  • (原)本想说脏话,奈何已放下
  • (轉)JSON.stringify 语法实例讲解
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .NET I/O 学习笔记:对文件和目录进行解压缩操作
  • .net 怎么循环得到数组里的值_关于js数组
  • .NET连接数据库方式
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • @Async注解的坑,小心
  • @font-face 用字体画图标
  • @LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析