概述
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<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();
}
}
}
如此,就完成了一个简单的缓存类
网友评论