java 缓存工具类_Java并发编程:ConcurrentHashMap的使用,实现一个简单的缓存工具类...

概述

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 +

'}';

}

}

创建过期缓存清理线程类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();

}

}

}

}

创建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 CACHE_OBJECT_MAP = new ConcurrentHashMap<>();

/**

* 这个记录了缓存使用的最后一次的记录,最近使用的在最前面

*/

private static final List 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 deleteKeyList = new LinkedList<>();

for(Map.Entry 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();

}

}

}

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值