Java ConcurrentHashMap删除密钥
我试图找出如何编写线程安全、过期的条目和缓存。此缓存将用作Java ConcurrentHashMap删除密钥,java,concurrency,Java,Concurrency,我试图找出如何编写线程安全、过期的条目和缓存。此缓存将用作无命中缓存,因此如果在某些存储中找不到条目,我会将其放入此缓存中,并在接下来的几分钟内避免后续调用 将有多个线程读取和写入此缓存 在我的应用程序中只有一个ThreadSafeCache实例 我不确定删除contains方法中的条目是否会出现同步问题 如何测试这个类的线程安全性 问候 public class ThreadSafeCache { private final Clock clock = Clock.systemUTC
无命中
缓存,因此如果在某些存储中找不到条目,我会将其放入此缓存中,并在接下来的几分钟内避免后续调用
将有多个线程读取和写入此缓存
在我的应用程序中只有一个ThreadSafeCache实例
我不确定删除contains方法中的条目是否会出现同步问题
如何测试这个类的线程安全性
问候
public class ThreadSafeCache
{
private final Clock clock = Clock.systemUTC();
private final Duration expiration = Duration.ofMinutes(10);
private final ConcurrentHashMap<CacheKey, CacheValue> internalMap = new ConcurrentHashMap<>();
public boolean contains(String a, String b, byte[] c, String d)
{
CacheKey key = new CacheKey(a, b, c, d);
CacheValue value = internalMap.get(key);
if (value == null || value.isExpired())
{
internalMap.remove(key);
return false;
}
return true;
}
public void put(String a, String b, byte[] c, String d)
{
internalMap.computeIfAbsent(new CacheKey(a, b, c, d), key -> new CacheValue());
}
private class CacheValue
{
private final Instant insertionDate;
private CacheValue()
{
this.insertionDate = clock.instant();
}
boolean isExpired()
{
return Duration.between(insertionDate,
clock.instant()).compareTo(expiration) > 0;
}
}
}
公共类ThreadSafeCache
{
专用最终时钟=Clock.systemUTC();
私人最终期限到期=分钟的期限(10);
私有最终ConcurrentHashMap internalMap=新ConcurrentHashMap();
公共布尔包含(字符串a、字符串b、字节[]c、字符串d)
{
CacheKey=新的CacheKey(a、b、c、d);
CacheValue=internalMap.get(键);
if(value==null | | value.isExpired())
{
删除(键);
返回false;
}
返回true;
}
公共void put(字符串a、字符串b、字节[]c、字符串d)
{
computeIfAbsent(新缓存键(a、b、c、d),键->新缓存值());
}
私有类缓存值
{
私人最终即时插入;
私有缓存值()
{
this.insertionDate=clock.instant();
}
布尔isExpired()
{
返回持续时间。介于(插入日期,
clock.instant()).compareTo(过期)>0;
}
}
}
您在同一个函数中调用了两个映射操作,这意味着存在交错的范围(即,在函数中的两个操作之间发生另一个操作,改变其行为)。要解决此问题,可以将映射操作放在synchronized(internalMap){}
块中。注意,您必须对在两个离散方法调用中与映射交互的任何方法执行此操作
从代码样式的角度来看,在contains
方法中修改映射是不好的做法。这将降低代码的可预测性。另一个人第一次访问您的代码(或者几个月后您)可能不记得contains()
实际上修改了缓存<代码>包含意味着它只是检查缓存,而不是修改缓存
我的建议是:
- 如果密钥已过期,只需返回false即可
- 在
get()
- 您的问题:“我不确定删除contains方法中的条目是否会出现同步问题”
=>删除操作没有问题,因为您使用的是同步集合ConcurrentHashMap,它是最佳选择
额外:获取同步集合的另一种方法是:collections.synchonize(myCollection),但如果我们在多线程环境中使用remove操作(可能在循环中),它会抛出ModificationException
因此,通常建议使用同步集合(例如:ConcurrentHashMap)我同意命名部分,但对于并发部分,我不同意。在
contains()
OP中,OP正在非同步方法中对映射执行相互依赖的两个操作。我不认为这是线程安全的。我完全错过了。也许该睡觉了,谢谢你指出这一点。我会编辑我的答案。不确定你到底在建什么,但以防万一,你是否考虑过使用谷歌的番石榴?它似乎与您试图构建的内容相匹配。看看下面的链接:我没有使用番石榴缓存,因为我不知道把什么作为值,它实际上是空的。我试图避免昂贵的IO操作来获取空值,因此当昂贵的IO返回空值时,我只想记录它,并在接下来的几分钟内避免该空值。使用代理项,如Boolean.TRUE
。然后将番石榴/咖啡因与expireAfterWrite(duration)
一起使用,以获得等效的行为。在contains()中,
OP在地图上执行两个操作,这两个操作在非同步方法中相互依赖。执行两个相互依赖的线程安全方法不能保证是线程安全的