• ConcurrentHashMap 实现缓存类


    参考:https://blog.csdn.net/woshilijiuyi/article/details/81335497

    在规定时间内,使用 hashMap 实现一个缓存工具类,需要考虑一下几点

    1. 不可变对象
    2. 单例
    3. 线程安全
    4. 回收失效数据
    5. 垃圾回收
    6. 缓存大小
    7. LRU

    注备:

    • LRU: Least Recently Used ,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面淘汰。
    • OPT :  最佳置换算法,是一种理想情况下的置换算法,但实际上不可实现。思想是标记每个页面多久后被使用,最大的将被淘汰
    • FIFO:先进先出,建立一个FIFO 队列,收容所有在内存中的页,被置换的页总在队列头上进行。
    • LFU : 最少使用置换算法,使用最少使用置换算法在内存中的每个页面设置一个移位寄存器,记录页面被使用的频率。
    package com;
    
    import lombok.Data;
    import java.util.LinkedList;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    /**
     * Created by baizhuang on 2019/11/15 10:34.
     */
    
    public class CacheManager {
    
        private CacheManager() {
        }
    
        //是否开启清除失效缓存
        private volatile Boolean clearExpireCacheEnable = true;
    
        //缓存失效时间
        private long cacheTimeout = 12 * 60 * 60 * 1000L;
    
        //缓存使用记录
        private static LinkedList<Object> cacheUseRecord = new LinkedList<>();
    
        //可缓存最大数量
        private static Integer MAX_CACHE_SIZE = 80;
    
        //重入读写锁
        private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        private static Lock writeLock = reentrantReadWriteLock.writeLock();
        private static Lock readLock = reentrantReadWriteLock.readLock();
    
        private final static Map<Object, CacheEntry> cacheEntryMap = new ConcurrentHashMap<>();
    
    
        private void init() {
            initClearTask();
        }
    
        //自定义缓存失效时间
        private void init(long cacheTimes) {
            this.cacheTimeout = cacheTimes;
            initClearTask();
        }
    
        private void initClearTask() {
            //启动清除失效缓存数据
            if (clearExpireCacheEnable) {
                new ClearCacheTask().start();
            }
        }
    
    
        private static CacheManager getCacheManagerInstance() {
            return CacheManagerFactory.CACHE_MANAGER;
        }
    
        private static class CacheManagerFactory {
            private static final CacheManager CACHE_MANAGER = new CacheManager();
        }
    
        private class ClearCacheTask extends Thread {
    
            ClearCacheTask() {
                super.setName("clear cache task start ...");
            }
    
            @Override
            public void run() {
                while (clearExpireCacheEnable) {
                    try {
                        long now = System.currentTimeMillis();
    
                        //定时清理
                        try {
    //                        Thread.sleep(1000 * 60 * 60);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
    
                        cacheEntryMap.keySet().stream().forEach(key -> {
                            try {
                                writeLock.lock();
    
                                //判断使用记录中的key是否已经被LRU清除
                                if (!cacheUseRecord.contains(key)) {
                                    return;
                                }
    
                                CacheEntry entry = cacheEntryMap.get(key);
                                if (now - entry.lastTouchTime >= cacheTimeout) {
                                    cacheEntryMap.remove(key);
                                    cacheUseRecord.remove(key);
                                    System.out.println("清理缓存key:" + key);
    
                                }
                            } finally {
                                writeLock.unlock();
                            }
                        });
    
                        Thread.sleep(cacheTimeout);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    
        //失效时间,value,entry,可根据需要决定是否继承Map.Entry<K,V>
        @Data
        private class CacheEntry {
            long lastTouchTime;
    
            Object value;
    
            CacheEntry(Object value) {
                super();
                this.value = value;
                this.lastTouchTime = System.currentTimeMillis();
            }
        }
    
    
        public Object get(Object key) {
    
            readLock.lock();
            CacheEntry entry = null;
            try {
                entry = cacheEntryMap.get(key);
            } finally {
                readLock.unlock();
            }
            if (null == entry)
                return null;
    
            //更新缓存访问时间
            touchCache(entry);
            //更新使用记录
            touchUseRecord(key);
    
            return entry == null ? null : entry.value;
        }
    
        //更新缓存访问时间
        public static void touchCache(CacheEntry entry) {
            writeLock.lock();
            try {
                entry.setLastTouchTime(System.currentTimeMillis());
            } finally {
                writeLock.unlock();
            }
    
        }
    
        //更新缓存使用记录
        public static void touchUseRecord(Object key) {
    
            writeLock.lock();
            try {
                //删除使用记录
                cacheUseRecord.remove(key);
                //新增使用记录到首位
                cacheUseRecord.add(0, key);
            } finally {
                writeLock.unlock();
            }
        }
    
    
        public Object put(Object key, Object value) throws Exception {
    
            //判断缓存大小是否够用,否则根据LRU删除最久未使用的元素
            if (cacheEntryMap.size() > MAX_CACHE_SIZE) {
                deleteLRU();
            }
            if (cacheEntryMap.size() > MAX_CACHE_SIZE) {
                throw new Exception("缓存大小超出限制");
            }
    
            CacheEntry entry = new CacheEntry(value);
    
            writeLock.lock();
            try {
                cacheEntryMap.put(key, entry);
                cacheUseRecord.add(0, key);
            } finally {
                writeLock.unlock();
            }
            return value;
        }
    
    
        /**
         * 删除最近最久未使用的缓存
         */
        public static void deleteLRU() {
    
            Object cacheKey = null;
            writeLock.lock();
            try {
                cacheKey = cacheUseRecord.remove(cacheUseRecord.size() - 1);
                cacheEntryMap.remove(cacheKey);
                System.out.println("LRU清除元素key:" + cacheKey);
            } finally {
                writeLock.unlock();
            }
        }
    
        public static void delete(Object key) {
    
            if (null == key)
                return;
    
            writeLock.lock();
            try {
                cacheEntryMap.remove(key);
                cacheUseRecord.remove(key);
            } finally {
                writeLock.unlock();
            }
        }
    
    
        public static void clear() {
    
            writeLock.lock();
            try {
                cacheEntryMap.clear();
                cacheUseRecord.clear();
            } finally {
                writeLock.unlock();
            }
        }
    
    
        public static void main(String[] args) throws Exception {
            CacheManager cacheManager = CacheManager.getCacheManagerInstance();
            cacheManager.init(0);
    
            for (int i = 0; i < 200; i++) {
                cacheManager.put(i + "", i);
            }
        }
    }
  • 相关阅读:
    机器学习——线性回归
    系统课程
    C++——数据结构之链表
    PHP检测json格式数据
    如何搭建git服务器
    ucenter通信实现同步登录、同步退出(详细)
    获取ip地址
    JQuery对象与DOM对象的区别与转换
    php导出数据到csv文件
    [转载]php中sleep,flush,ob_flush函数介绍
  • 原文地址:https://www.cnblogs.com/baizhuang/p/11865261.html
Copyright © 2020-2023  润新知