美文网首页面试精选
Java并发编程:ConcurrentHashMap的使用,实现

Java并发编程:ConcurrentHashMap的使用,实现

作者: singleZhang2010 | 来源:发表于2020-12-24 16:56 被阅读0次

    概述

    ConcurrentHashMap 是线程安全且高效的HashMap

    问:为什么使用ConcurrentHashMap?

    在并发编程中使用HashMap,会出现ConcurrentModificationException异常、会引起程序死循环等情况。使用线程安全的Hashtable的话效率太低,ConcurrentHashMap保证了线程安全的同时又保证了高效的操作。

    多线程下,HashMap为什么会出现死循环?

    HashMap出现死循环java7和java8不太一样。
    java7出现在扩容的场合,因为头插法导致链表死循环,java8使用了数组+链表+红黑树 以及尾插法修复了这个问题,不过在PutTreeValue的时候可能会出现Node节点转换为TreeNode结点异常,所以多线程下不要使用HashMap。

    使用ConcurrentHashMap实现本地缓存

    1.创建包:com.zhxin.threadLab.concurrenthashmap.chapter1,并创建缓存实体类CacheObj

    package com.zhxin.threadLab.concurrenthashmap.chapter1;
    
    /**
     * @ClassName CacheObj
     * @Description //缓存对象类
     * @Author singleZhang
     * @Email 405780096@qq.com
     * @Date 2020/12/24 0024 下午 3:30
     **/
    public class CacheObj {
        /**
         * 缓存对象
         */
        private Object CacheValue;
        /**
         * 缓存过期时间
         */
        private Long expireTime;
    
        CacheObj(Object cacheValue, Long expireTime) {
            CacheValue = cacheValue;
            this.expireTime = expireTime;
        }
    
        Object getCacheValue() {
            return CacheValue;
        }
    
        Long getExpireTime() {
            return expireTime;
        }
    
        @Override
        public String toString() {
            return "CacheObj {" +
                    "CacheValue = " + CacheValue +
                    ", expireTime = " + expireTime +
                    '}';
        }
    }
    
    
    1. 创建过期缓存清理线程类CleanCacheThread
    package com.zhxin.threadLab.concurrenthashmap.chapter1;
    
    /**
     * @ClassName CleanCacheThread
     * @Description //清理过期缓存线程
     * @Author singleZhang
     * @Email 405780096@qq.com
     * @Date 2020/12/24 0024 下午 3:37
     **/
    public class CleanCacheThread implements Runnable{
    
        @Override
        public void run() {
            ConcurrentHashMapCacheUtil.setCleanThreadRun();
            while (true) {
                System.out.println("clean thread run ");
                ConcurrentHashMapCacheUtil.deleteTimeOut();
                try {
                    Thread.sleep(ConcurrentHashMapCacheUtil.ONE_MINUTE);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    
    1. 创建ConcurrentHashMap缓存工具类ConcurrentHashMapCacheUtil
    package com.zhxin.threadLab.concurrenthashmap.chapter1;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @ClassName ConcurrentHashMapCacheUtil
     * @Description //使用ConcurrentHashMap 做本地缓存
     * @Author singleZhang
     * @Email 405780096@qq.com
     * @Date 2020/12/24 0024 下午 3:23
     **/
    public class ConcurrentHashMapCacheUtil {
    
        private static Logger LOGGER = LoggerFactory.getLogger(ConcurrentHashMapCacheUtil.class);
    
        /**
         * 缓存最大个数
         */
        private static final Integer CACHE_MAX_NUMBER = 1000;
        /**
         * 当前缓存个数
         */
        private static Integer CURRENT_SIZE = 0;
        /**
         * 时间一分钟
         */
        static final Long ONE_MINUTE = 60 * 1000L;
        /**
         * 缓存对象
         */
        private static final Map<String, CacheObj> CACHE_OBJECT_MAP = new ConcurrentHashMap<>();
        /**
         * 这个记录了缓存使用的最后一次的记录,最近使用的在最前面
         */
        private static final List<String> CACHE_USE_LOG_LIST = new LinkedList<>();
        /**
         * 清理过期缓存是否在运行
         */
        private static volatile Boolean CLEAN_THREAD_IS_RUN = false;
    
        /**
         * 设置缓存
         */
        public static void setCache(String cacheKey, Object cacheValue, long cacheTime) {
            Long expireTime = null;
            if (cacheTime <= 0L) {
                if (cacheTime == -1L) {
                    expireTime = -1L;
                } else {
                    return;
                }
            }
            checkSize();
            saveCacheUseLog(cacheKey);
            CURRENT_SIZE = CURRENT_SIZE + 1;
            if (expireTime == null) {
                expireTime = System.currentTimeMillis() + cacheTime;
            }
            CacheObj cacheObj = new CacheObj(cacheValue, expireTime);
            CACHE_OBJECT_MAP.put(cacheKey, cacheObj);
            LOGGER.info("have set key :" + cacheKey);
        }
        /**
         * 设置缓存
         */
        public static void setCache(String cacheKey, Object cacheValue) {
            setCache(cacheKey, cacheValue, -1L);
        }
        /**
         * 获取缓存
         */
        public static Object getCache(String cacheKey) {
            startCleanThread();
            if (checkCache(cacheKey)) {
                saveCacheUseLog(cacheKey);
                return CACHE_OBJECT_MAP.get(cacheKey).getCacheValue();
            }
            return null;
        }
        public static boolean isExist(String cacheKey) {
            return checkCache(cacheKey);
        }
        /**
         * 删除所有缓存
         */
        public static void clear() {
            LOGGER.info("have clean all key !");
            CACHE_OBJECT_MAP.clear();
            CURRENT_SIZE = 0;
        }
        /**
         * 删除某个缓存
         */
        public static void deleteCache(String cacheKey) {
            Object cacheValue = CACHE_OBJECT_MAP.remove(cacheKey);
            if (cacheValue != null) {
                LOGGER.info("have delete key :" + cacheKey);
                CURRENT_SIZE = CURRENT_SIZE - 1;
            }
        }
        /**
         * 判断缓存在不在,过没过期
         */
        private static boolean checkCache(String cacheKey) {
            CacheObj cacheObj = CACHE_OBJECT_MAP.get(cacheKey);
            if (cacheObj == null) {
                return false;
            }
            if (cacheObj.getExpireTime() == -1L) {
                return true;
            }
            if (cacheObj.getExpireTime() < System.currentTimeMillis()) {
                deleteCache(cacheKey);
                return false;
            }
            return true;
        }
        /**
         * 删除最近最久未使用的缓存
         */
        private static void deleteLRU() {
            LOGGER.info("delete Least recently used run!");
            String cacheKey = null;
            synchronized (CACHE_USE_LOG_LIST) {
                if (CACHE_USE_LOG_LIST.size() >= CACHE_MAX_NUMBER - 10) {
                    cacheKey = CACHE_USE_LOG_LIST.remove(CACHE_USE_LOG_LIST.size() - 1);
                }
            }
            if (cacheKey != null) {
                deleteCache(cacheKey);
            }
        }
    
        /**
         * 删除过期的缓存
         */
        static void deleteTimeOut() {
            LOGGER.info("delete time out run!");
            List<String> deleteKeyList = new LinkedList<>();
            for(Map.Entry<String, CacheObj> entry : CACHE_OBJECT_MAP.entrySet()) {
                if (entry.getValue().getExpireTime() < System.currentTimeMillis() && entry.getValue().getExpireTime() != -1L) {
                    deleteKeyList.add(entry.getKey());
                }
            }
            for (String deleteKey : deleteKeyList) {
                deleteCache(deleteKey);
            }
            LOGGER.info("delete cache count is :" + deleteKeyList.size());
        }
        /**
         * 检查大小
         * 当当前大小如果已经达到最大大小
         * 首先删除过期缓存,如果过期缓存删除过后还是达到最大缓存数目
         * 删除最久未使用缓存
         */
        private static void checkSize() {
            if (CURRENT_SIZE >= CACHE_MAX_NUMBER) {
                deleteTimeOut();
            }
            if (CURRENT_SIZE >= CACHE_MAX_NUMBER) {
                deleteLRU();
            }
        }
    
        /**
         * 保存缓存的使用记录
         */
        private static synchronized void saveCacheUseLog(String cacheKey) {
            synchronized (CACHE_USE_LOG_LIST) {
                CACHE_USE_LOG_LIST.remove(cacheKey);
                CACHE_USE_LOG_LIST.add(0,cacheKey);
            }
        }
    
        /**
         * 设置清理线程的运行状态为正在运行
         */
        static void setCleanThreadRun() {
            CLEAN_THREAD_IS_RUN = true;
        }
    
        /**
         * 开启清理过期缓存的线程
         */
        private static void startCleanThread() {
            if (!CLEAN_THREAD_IS_RUN) {
                CleanCacheThread cleanCacheThread = new CleanCacheThread();
                Thread thread = new Thread(cleanCacheThread);
                //设置为后台守护线程
                thread.setDaemon(true);
                thread.start();
            }
        }
    }
    

    如此,就完成了一个简单的缓存类

    相关文章

      网友评论

        本文标题:Java并发编程:ConcurrentHashMap的使用,实现

        本文链接:https://www.haomeiwen.com/subject/hjpswktx.html