Java过期时间缓存-ConcurrentHashMap实现

LocalCache 实现代码

package xyz.biandeshen.commonstests.util;


import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author fjp
 * @Title: LiteCache
 * @ProjectName commons-tests
 * @Description: 带有过期时间的缓存, 过期队列以延时队列实现
 * @date 2019/11/1816:24
 */
@SuppressWarnings("all")
public class LocalCache2 {
	/**
	 * 缓存最大个数
	 */
	private final Integer CACHE_MAX_SIZE;
	/**
	 * 过期时间,默认一分钟
	 */
	private final Long EXPIRATION_TIME;
	
	/**
	 * 过期管理线程池
	 */
	private final CacheAndExpirationQueueManagementThreadPool cacheAndExpirationQueueManagementThreadPool;
	/**
	 * 缓存
	 */
	private final ConcurrentMap<String, Node> cache;
	/**
	 * 到期队列
	 */
	private final DelayQueue<Node> expireQueue;
	/**
	 * 锁
	 */
	private final ReentrantLock reentrantLock;
	
	
	private LocalCache2(Builder builder) {
		//缓存及队列等参数赋值
		this.CACHE_MAX_SIZE = builder.CACHE_MAX_SIZE;
		this.EXPIRATION_TIME = builder.EXPIRATION_TIME;
		//缓存及过期队列初始化
		cache = new ConcurrentHashMap<>(CACHE_MAX_SIZE);
		expireQueue = new DelayQueue<Node>();
		reentrantLock = new ReentrantLock();
		
		//创建缓存及过期队列管理线程池
		this.cacheAndExpirationQueueManagementThreadPool =
				builder.cacheAndExpirationQueueManagementThreadPool;
	}
	
	/**
	 * 执行清理线程
	 */
	public void scheduleAtFixedRate() {
		//清理线程
		CleanExpiredNodeWorker cacheCleaner = new CleanExpiredNodeWorker();
		//执行定时清理任务
		cacheAndExpirationQueueManagementThreadPool.scheduleAtFixedRate(cacheCleaner);
	}
	
	static class Builder {
		/**
		 * 缓存最大个数
		 */
		private Integer CACHE_MAX_SIZE = 2 << 10;
		/**
		 * 过期时间,默认一分钟
		 */
		private Long EXPIRATION_TIME = TimeUnit.MINUTES.toMillis(1);
		/**
		 * 过期管理线程池
		 */
		private CacheAndExpirationQueueManagementThreadPool cacheAndExpirationQueueManagementThreadPool =
				new CacheAndExpirationQueueManagementThreadPool.Builder().build();
		
		public Builder() {
		
		}
		
		/**
		 * LocalCache参数设置
		 *
		 * @param cacheMaxSize
		 * 		缓存大小
		 * @param expiredQueueSize
		 * 		过期队列大小
		 * @param expriationTime
		 * 		过期时间(即保存缓存时长)
		 * @param threadPool
		 * 		过期管理线程池
		 */
		public Builder(Integer cacheMaxSize, Long expriationTime,
		               CacheAndExpirationQueueManagementThreadPool threadPool) {
			this.CACHE_MAX_SIZE = cacheMaxSize;
			this.EXPIRATION_TIME = expriationTime;
			this.cacheAndExpirationQueueManagementThreadPool = threadPool;
		}
		
		public Builder cacheMaxSize(Integer cacheMaxSize) {
			this.CACHE_MAX_SIZE = cacheMaxSize;
			return this;
		}
		
		public Builder expriationTime(Long expriationTime) {
			this.EXPIRATION_TIME = expriationTime;
			return this;
		}
		
		public Builder threadPool(CacheAndExpirationQueueManagementThreadPool threadPool) {
			this.cacheAndExpirationQueueManagementThreadPool = threadPool;
			return this;
		}
		
		public LocalCache2 build() {
			return new LocalCache2(this);
		}
		
	}
	
	/**
	 * 设置缓存
	 */
	public Object set(String key, Object value, long cacheTime) {
		//判断缓存中是否存在
		Node node = new Node(key, value, (System.currentTimeMillis() + TimeUnit.MILLISECONDS.toMillis(cacheTime)));
		//加锁判断,锁定过期队列,缓存则使用原子操作
		reentrantLock.lock();
		try {
			Node putIfAbsent = cache.putIfAbsent(key, node);
			// 为空,则代表不存在,需在过期队列添加,过期队列的大小可能大于缓存
			expireQueue.put(node);
			if (putIfAbsent != null) {
				// 否则,代表缓存中已存在(已更新)过期队列中需要更新时间
				// 移除已有的
				expireQueue.remove(node);
			}
		} finally {
			//释放锁
			reentrantLock.unlock();
		}
		return node.value;
	}
	
	/**
	 * 设置缓存
	 */
	public Object set(String key, Object value) {
		return set(key, value, EXPIRATION_TIME);
	}
	
	/**
	 * 获取缓存
	 */
	public Object get(String key) {
		Node node = cache.get(key);
		// 加锁
		reentrantLock.lock();
		try {
			// 不为空,则更新
			if (node != null) {
				// 更新过期时间
				expireQueue.remove(node);
				// 延长过期时间
				node.expireTime = (System.currentTimeMillis() + TimeUnit.MILLISECONDS.toMillis(EXPIRATION_TIME));
				expireQueue.put(node);
			}
		} finally {
			reentrantLock.unlock();
		}
		return node == null ? null : node.value;
	}
	
	/**
	 * 移除缓存,并返回对应key的值
	 */
	public Object remove(String key) {
		Node node = cache.remove(key);
		//	加锁
		reentrantLock.lock();
		try {
			//	不为空,则移除
			if (node != null) {
				expireQueue.remove(node);
			}
		} finally {
			reentrantLock.unlock();
		}
		return node == null ? null : node.value;
	}
	
	/**
	 * 缓存及过期队列管理线程池
	 */
	@SuppressWarnings("all")
	static class CacheAndExpirationQueueManagementThreadPool {
		/**
		 * 缓存清理线程池名称
		 */
		private final String THREAD_POOL_NAME;
		/**
		 * 缓存清理线程池最小线程数
		 */
		private final int THREAD_POOL_CORESIZE;
		/**
		 * 缓存清理线程池初始延迟时间
		 * 指定时延后开始执行任务,以后每隔period的时长再次执行该任务
		 */
		private final Long THREAD_RATE_TIME;
		/**
		 * 每隔period的时长再次执行该任务
		 */
		private final Long THREAD_PERIOD_TIME;
		/**
		 * 线程工厂,为线程命名
		 */
		private final ThreadFactory namedThreadFactory;
		/**
		 * 计划执行者服务-过期管理线程池
		 */
		private final ScheduledExecutorService expirationManagementThreadPool;
		
		/**
		 * 清理线程是否设置为守护线程(默认为true)
		 */
		private final boolean THREAD_DEAMON;
		
		private CacheAndExpirationQueueManagementThreadPool(Builder builder) {
			this.THREAD_POOL_NAME = builder.THREAD_POOL_NAME;
			this.THREAD_POOL_CORESIZE = builder.THREAD_POOL_CORESIZE;
			this.THREAD_RATE_TIME = builder.THREAD_RATE_TIME;
			this.THREAD_PERIOD_TIME = builder.THREAD_PERIOD_TIME;
			this.THREAD_DEAMON = builder.THREAD_DEAMON;
			namedThreadFactory = new ThreadFactoryBuilder()
					                     .setNameFormat(THREAD_POOL_NAME).setDaemon(THREAD_DEAMON).build();
			expirationManagementThreadPool = new ScheduledThreadPoolExecutor(THREAD_POOL_CORESIZE, namedThreadFactory);
		}
		
		private void scheduleAtFixedRate(Runnable runnable) {
			expirationManagementThreadPool.scheduleAtFixedRate(runnable, THREAD_RATE_TIME, THREAD_PERIOD_TIME,
			                                                   TimeUnit.MILLISECONDS);
		}
		
		static class Builder {
			/**
			 * 缓存清理线程池名称
			 */
			private String THREAD_POOL_NAME = "cache-pool-%d";
			/**
			 * 缓存清理线程池最小线程数
			 */
			private int THREAD_POOL_CORESIZE = 10;
			/**
			 * 缓存清理线程池初始延迟时间(默认一分钟)
			 * 指定时延后开始执行任务,以后每隔period的时长再次执行该任务
			 */
			private Long THREAD_RATE_TIME = TimeUnit.MINUTES.toMillis(1);
			/**
			 * 每隔period的时长再次执行该任务(默认一分钟)
			 */
			private Long THREAD_PERIOD_TIME = TimeUnit.MINUTES.toMillis(1);
			/**
			 * 清理线程是否设置为守护线程(默认为true)
			 */
			private boolean THREAD_DEAMON = true;
			
			/**
			 * 缓存清理线程池参数设置
			 *
			 * @param threadPoolName
			 * 		线程池名
			 * @param threadPoolCoreSize
			 * 		线程池大小
			 * @param threadRateTime
			 * 		缓存清理线程池初始延迟时间(默认一分钟)
			 * @param threadPeriodTime
			 * 		每隔period的时长再次执行该任务(默认一分钟)
			 * @param deamon
			 * 		清理线程是否设置为守护线程(默认为true)
			 */
			public Builder(String threadPoolName, int threadPoolCoreSize, Long threadRateTime,
			               Long threadPeriodTime, boolean deamon) {
				this.THREAD_POOL_NAME = threadPoolName;
				this.THREAD_POOL_CORESIZE = threadPoolCoreSize;
				this.THREAD_RATE_TIME = threadRateTime;
				this.THREAD_PERIOD_TIME = threadPeriodTime;
				this.THREAD_DEAMON = deamon;
			}
			
			public Builder() {
			}
			
			public Builder threadPoolName(String threadPoolName) {
				this.THREAD_POOL_NAME = threadPoolName;
				return this;
			}
			
			public Builder threadPoolCoreSize(int threadPoolCoreSize) {
				this.THREAD_POOL_CORESIZE = threadPoolCoreSize;
				return this;
			}
			
			public Builder threadRateTime(Long threadRateTime) {
				this.THREAD_RATE_TIME = threadRateTime;
				return this;
			}
			
			public Builder threadPeriodTime(Long threadPeriodTime) {
				this.THREAD_PERIOD_TIME = threadPeriodTime;
				return this;
			}
			
			public Builder deamon(boolean deamon) {
				this.THREAD_DEAMON = deamon;
				return this;
			}
			
			public CacheAndExpirationQueueManagementThreadPool build() {
				return new CacheAndExpirationQueueManagementThreadPool(this);
			}
		}
	}
	
	/**
	 * 删除已过期的数据(默认实现)
	 */
	private class CleanExpiredNodeWorker implements Runnable {
		@Override
		public void run() {
			long now = System.currentTimeMillis();
			while (true) {
				//reentrantLock.lock();
				try {
					Node node = expireQueue.take();
					//	否则,移除
					if (node != null) {
						cache.remove(node.key);
					}
				} catch (InterruptedException e) {
					Thread.currentThread().interrupt();
				} /*finally {
					reentrantLock.unlock();
				}*/
			}
		}
		
	}
	
	/**
	 * 数据存储节点
	 */
	private static class Node implements Delayed {
		// key
		private final String key;
		// value
		private final Object value;
		// 预计过期时间 : 当前时间 + 过期时间
		private long expireTime;
		
		Node(String key, Object value, long expireTime) {
			this.key = key;
			this.value = value;
			this.expireTime = expireTime;
		}
		
		
		@Override
		public boolean equals(Object object) {
			if (this == object) {
				return true;
			}
			if (object == null || getClass() != object.getClass()) {
				return false;
			}
			Node node = (Node) object;
			return expireTime == node.expireTime &&
					       key.equals(node.key) &&
					       value.equals(node.value);
		}
		
		@Override
		public int hashCode() {
			return Objects.hash(key, value, expireTime);
		}
		
		@Override
		public long getDelay(TimeUnit unit) {
			return expireTime - System.currentTimeMillis();
		}
		
		@Override
		public int compareTo(Delayed delayed) {
			Node node = (Node) delayed;
			if (node != null) {
				return Math.toIntExact(this.expireTime - node.expireTime);
			}
			return -1;
		}
	}
	先进先出实体
	//class FIFOEntry<E extends Comparable<? super E>> implements Comparable<FIFOEntry<E>> {
	//	final AtomicLong seq = new AtomicLong(0);
	//	final long seqNum;
	//	final E entry;
	//
	//	public FIFOEntry(E entry) {
	//		seqNum = seq.getAndIncrement();
	//		this.entry = entry;
	//	}
	//
	//	public E getEntry() {
	//		return entry;
	//	}
	//
	//	@Override
	//	public int compareTo(FIFOEntry<E> other) {
	//		int res = entry.compareTo(other.entry);
	//		if (res == 0 && other.entry != this.entry) {
	//			res = (seqNum < other.seqNum ? -1 : 1);
	//		}
	//		return res;
	//	}
	//}
}

 LocalCache测试代码

package xyz.biandeshen.commonstests.util;

import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.biandeshen.commonstests.util.LocalCache2.CacheAndExpirationQueueManagementThreadPool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author fjp
 * @Title: LocalCacheTest
 * @ProjectName commons-tests
 * @Description: LocalCacheTest
 * @date 2019/11/209:42
 */
@SuppressWarnings("all")
public class LocalCacheTest2 {
	public static void main(String[] args) throws InterruptedException {
		Logger logger = LoggerFactory.getLogger(LocalCacheTest2.class);
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		//LocalCache localCache =
		//		new LocalCache.Builder().expriationTime(TimeUnit.MINUTES.toMillis(1)).threadPool(
		//				new CacheAndExpirationQueueManagementThreadPool.Builder().deamon(true).build()).build();
		/*
		 LocalCache 使用示例
		 */
		LocalCache2 localCache = new LocalCache2.Builder(1024, TimeUnit.MINUTES.toMillis(1),
		                                                 new CacheAndExpirationQueueManagementThreadPool.Builder(
				                                                 "clean-cache-pool",
				                                                 10,
				                                                 TimeUnit.MINUTES.toMillis(1),
				                                                 TimeUnit.MINUTES.toMillis(1),
				                                                 true)
				                                                 .build()).build();
		//执行定时清理线程
		localCache.scheduleAtFixedRate();
		
		executorService.execute(() -> {
			for (int i = 0; i < 1000000; i++) {
				Object set = localCache.set(String.valueOf(i), RandomStringUtils.random(10));
				logger.info("{} {}", Thread.currentThread().getName(), set);
			}
		});
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		executorService.execute(() -> {
			for (int i = 0; i < 1000000; i++) {
				Object key = localCache.get(String.valueOf(i));
				logger.error("{} {}", Thread.currentThread().getName(), key);
			}
		});
		executorService.execute(() -> {
			for (int i = 0; i < 1000000; i++) {
				Object key = localCache.get(String.valueOf(i));
				logger.warn("{} {}", Thread.currentThread().getName(), key);
			}
		});
		executorService.awaitTermination(10, TimeUnit.SECONDS);
		executorService.shutdownNow();
	}
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现过期时间缓存,可以使用 `ConcurrentHashMap` 结合 `ScheduledExecutorService` 来实现。 首先,使用 `ConcurrentHashMap` 存储缓存的键值对,键为缓存的 key,值为缓存的 value。 然后,使用 `ScheduledExecutorService` 定时清除过期缓存。可以通过定时任务调度线程池来实现。 下面是一个简单的示例代码: ```java import java.util.Map; import java.util.concurrent.*; public class ExpiringCache<K, V> { private final Map<K, V> cache; private final ScheduledExecutorService scheduler; public ExpiringCache() { this.cache = new ConcurrentHashMap<>(); this.scheduler = Executors.newSingleThreadScheduledExecutor(); } // 添加缓存项 public void put(K key, V value, long expireTimeMillis) { cache.put(key, value); // 在指定时间后清除缓存项 scheduler.schedule(() -> cache.remove(key), expireTimeMillis, TimeUnit.MILLISECONDS); } // 获取缓存项 public V get(K key) { return cache.get(key); } // 移除缓存项 public void remove(K key) { cache.remove(key); } } ``` 使用示例: ```java ExpiringCache<String, String> cache = new ExpiringCache<>(); cache.put("key1", "value1", 5000); // 缓存项在5秒后过期 cache.put("key2", "value2", 10000); // 缓存项在10秒后过期 String value1 = cache.get("key1"); // 获取缓存项,如果过期则返回 null String value2 = cache.get("key2"); System.out.println(value1); // 输出:value1 System.out.println(value2); // 输出:value2 Thread.sleep(6000); String expiredValue = cache.get("key1"); System.out.println(expiredValue); // 输出:null,缓存项已过期 ``` 注意,该示例代码只是一个简单的实现,没有考虑并发情况下的线程安全性和性能优化,如果在实际项目中使用,请根据需求进行适当的改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值