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在地图上执行两个操作,这两个操作在非同步方法中相互依赖。执行两个相互依赖的线程安全方法不能保证是线程安全的