java缓存怎么实现,java实现一个本地缓存
缓存,我相信大家都很熟悉。在一个项目中,缓存肯定是必不可少的。市面上有很多缓存工具,比如Redis,Guava Cache或者EHcache。
如何解决写爬虫IP受阻的问题?立即使用。
这些工具我想大家都很熟悉了,今天就不多说了。先说一下如何实现本地缓存。参考以上工具,要实现更好的本地缓存,平头哥认为要从以下三个方面入手。
1、存储集合的选择
要实现本地缓存,存储容器必须是键/值形式的数据结构。在Java中,它也是我们常用的地图集合。HashMap有几种,Hashtable和ConcurrentHashMap供我们选择。如果不考虑高并发下的数据安全,我们可以选择HashMap。如果考虑高并发下的数据安全,可以选择Hashtable和ConcurrentHashMap中的一个。但是我们更喜欢ConcurrentHashMap,因为ConcurrentHashMap的性能比Hashtable好。
2、过期缓存处理
因为缓存是直接存储在内存中的,如果我们不处理过期的缓存,内存就会被大量无效的缓存占用,这不是我们想要的,所以我们需要清理这些无效的缓存。过期缓存处理可以参考Redis的策略来实现,该策略采用定期删懒和消除懒的策略。
定期删除策略
定期删除策略是每隔一段时间检测一次过期的缓存并将其删除。这种策略的优点是可以确保删除所有过期的缓存。同时也有不利之处。过期的缓存可能没有及时删除,这和我们设置的定时频率有关系。还有一个缺点就是,如果缓存的数据很多,每次测试也会给杯赛带来很大的压力。
懒惰淘汰策略
懒惰策略是在使用缓存时判断缓存是否过期,如果过期则删除,返回null。这种策略的好处是,只有搜索的时候,才能判断是否过期,对杯子影响很大。同时,这种策略有致命的缺点。当存储了大量缓存时,这些缓存未被使用且过期,都会成为无效缓存。这些无效的缓存会占用你大量的内存空间,最终导致服务器内存溢出。
我们简单了解一下Redis的两种逾期缓存处理策略,每种策略各有利弊。所以在使用过程中可以将两种策略结合起来,结合效果还是很理想的。
3、缓存淘汰策略
缓存过时不同于过期缓存处理。缓存过时意味着当我们的缓存数量达到我们指定的数量时,毕竟我们的内存不是无限的。如果需要继续添加缓存,就需要按照一定的策略让现有缓存中的一些缓存退役,为新添加的缓存腾出空间。让我们了解几种常见的缓存失效策略。
先进先出策略
当缓存空间不够时,先进入缓存的数据会先被清除,为新数据腾出空间。这个策略主要比较缓存元素的创建时间。在一些对数据有效性要求较高的场景下,可以考虑这种策略,首先保证最新数据的可用性。
最少使用策略
不管是否过期,根据元素被使用的次数,清除使用次数少的元素释放的空间。该策略主要比较元素的命中数,在保证高频数据有效性的场景下可以选择。
最近最少使用策略
不管是否过期,根据最后一次使用元素的时间戳,清除时间戳最远的元素空闲空间。这个策略主要比较get最后一次使用缓存的时间。更适用于热数据场景,首先保证热数据的有效性。
随机淘汰策略
无论缓存是否过期,它都将随机失效。如果对缓存数据没有要求,可以考虑这个策略。
不淘汰策略
当缓存达到指定值时,不会有缓存被停用,但不会添加新的缓存,并且在停用之前不会添加缓存。
上面是实现本地缓存需要考虑的三个点,看完我们应该知该如何实现一个本地缓存了,不妨我们一起来实现一个本地缓存。
实现本地缓存
在该演示中,我们采用实现队列作为存储集合,这样即使在高并发的情况下,我们也能够保证缓存的安全。过期缓存处理在这里我只使用了定时删除策略,并没有使用定时删除懒惰淘汰策略,你可以自己动手尝试一下使用这两种策略进行过期缓存处理。在缓存淘汰方面,我在这里采用了最少使用策略。好了,技术选型都知道了,我们一起来看看代码实现。
缓存对象类
公共类缓存实现ComparableCache{
//键
私有对象密钥;
//缓存值
私有对象值;
//最后一次访问时间
私有长访问时间;
//创建时间
私有长写时间;
//存活时间
私有长过期时间
//命中次数
私有整数点击次数;
.getter/setter().添加缓存
/**
* 添加缓存
*
* @param key
* @param值
*/
公共无效卖出(K密钥、五值、长期过期){
checkNotNull(key);
checkNotNull(值);
//当缓存存在时,更新缓存
if(并发hashmap。包含键(key)){
cache cache=并发hashmap。get(键);
缓存。sethitcount(缓存。gethitcount()1);
缓存。设置写入时间(系统。当前时间毫秒());
缓存。setaccesstime(系统。当前时间毫秒());
cache.setExpireTime(过期);
cache.setValue(值);
返回;
}
//已经达到最大缓存
if (isFull()) {
object kicked key=get kicked key();
如果(kickedKey!=null){
//移除最少使用的缓存
并发哈希表。移除(踢键);
}否则{
返回;
}
}
Cache Cache=new Cache();
缓存。setkey(键);
cache.setValue(值);
缓存。设置写入时间(系统。当前时间毫秒());
缓存。setaccesstime(系统。当前时间毫秒());
缓存。sethitcount(1);
cache.setExpireTime(过期);
concurrentHashMap.put(key,cache);
}获取缓存
/**
* 获取缓存
*
* @param key
* @返回
*/
公共对象获取(K密钥){
checkNotNull(key);
if (concurrentHashMap.isEmpty())返回空
如果(!并发哈希表。包含密钥(密钥))返回空
cache cache=并发hashmap。get(键);
if (cache==null)返回null
缓存。sethitcount(缓存。gethitcount()1);
缓存。setaccesstime(系统。当前时间毫秒());
返回缓存。getvalue();
}获取最少使用的缓存
/**
* 获取最少使用的缓存
* @返回
*/
私有对象getKickedKey() {
缓存最小值=集合。min(并发哈希表。values());
返回量滴getkey();
}过期缓存检测方法
/**
* 处理过期缓存
*/
类超时定时器线程实现可运行{
公共无效运行(){
while (true) {
尝试{
时间单位10.25秒。睡眠(60);
过期缓存();
} catch(异常e) {
e。printstacktrace();
}
}
}
/**
* 创建多久后,缓存失效
*
* @抛出异常
*/
私有void expireCache()引发异常{
System.out.println(检测缓存是否过期缓存);
for(Object key:concurrent hashmap。密钥集()){
cache cache=并发hashmap。get(键);
长时间超时=时间单位。纳秒。到秒(System.nanoTime()
缓存。获取写时间());
如果(缓存。getexpiretime()超时时间){
继续;
}
System.out.println(清除过期缓存: 键);
//清除过期缓存
并发哈希表。移除(键);
}
}
}以上就是实现爪哇本地缓存,该从这几点开始的详细内容,更多请关注我们其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。