ConcurrentHashMap 实现缓存类

Posted baizhuang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
        }
    }
}

 

以上是关于ConcurrentHashMap 实现缓存类的主要内容,如果未能解决你的问题,请参考以下文章

工具篇使用concurrentHashMap实现缓存工具类

基于ConcurrentHashMap 最简单无超时时间的本地缓存类

线程结果缓存实现(future与concurrenthashmap)

Map实现带失效时间的缓存工具类

并发编程中经常用到的ConcurrentHashMap

简单的Map缓存机制实现