具有过期可能性的简单Java字符串缓存

具有过期可能性的简单Java字符串缓存,java,concurrency,set,Java,Concurrency,Set,我正在为Java 1.5应用程序寻找一个具有过期功能的并发集。它将被用作存储/缓存在特定时间后过期的名称(即字符串值)的简单方法 我试图解决的问题是,两个线程在一定时间内不能使用相同的名称值(因此这是一个黑名单,确保相同的“名称”(类似于消息引用)在一定时间内不能被另一个线程重用。我自己不控制名称生成,因此我无法对实际名称/字符串执行唯一性,而应该将其视为一种限制/限制机制,以防止每秒多次使用相同的名称 例如: 线程#1执行缓存。添加(“unique_string,1),其中存储名称“uniqu

我正在为Java 1.5应用程序寻找一个具有过期功能的并发集。它将被用作存储/缓存在特定时间后过期的名称(即字符串值)的简单方法

我试图解决的问题是,两个线程在一定时间内不能使用相同的名称值(因此这是一个黑名单,确保相同的“名称”(类似于消息引用)在一定时间内不能被另一个线程重用。我自己不控制名称生成,因此我无法对实际名称/字符串执行唯一性,而应该将其视为一种限制/限制机制,以防止每秒多次使用相同的名称

例如: 线程#1执行缓存。添加(“unique_string,1),其中存储名称“unique_string”1秒。 如果任何线程在1秒内通过缓存.get(“unique\u string”)等操作查找“unique\u string”,它将得到肯定的响应(项目存在),但在此之后,该项目应过期并从集合中删除

该容器有时每秒可以处理50-100次插入/读取

我真的一直在寻找不同的解决方案,但没有找到任何我认为真正适合我需要的。这感觉像是一个简单的问题,但我找到的所有解决方案都太复杂或过火了


一个简单的想法是让一个
ConcurrentHashMap
对象的key设置为“name”,value设置为过期时间,然后一个线程每秒运行一次,并删除其值(过期时间)为的所有元素已经过去了,但我不确定这样做的效率有多高?我是否缺少一个更简单的解决方案?

一个简单的、唯一的、不会重复的字符串模式

private static final AtomicLong COUNTER = new AtomicLong(System.currentTimeMillis()*1000);
public static String generateId() {
     return Long.toString(COUNTER.getAndIncrement(), 36);
}
即使重新启动应用程序,也不会重复此操作

注意:将在以下步骤后重复:

  • 重新启动后,每秒生成的ID超过一百万个
  • 293年后。如果时间不够长,你可以将1000年减少到100年,得到2930年

谷歌的Guava库正好包含这样的缓存:。

这取决于-如果你需要严格的时间条件,或者软条件(比如1秒+/-20ms)。 此外,如果需要离散缓存失效或“按调用”

对于严格的条件,我建议添加一个不同的线程,它将使缓存每20毫秒失效一次


您还可以在存储的密钥时间戳中设置时间戳,并检查它是否过期。

使用线程执行器在项目过期的位置创建一个映射如何

//Declare your Map and executor service
final Map<String, ScheduledFuture<String>> cacheNames = new HashMap<String, ScheduledFuture<String>>();
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
//声明您的映射和执行器服务
final-Map cacheNames=new-HashMap();
ScheduledExecutorService executorService=Executors.newSingleThreadScheduledExecutor();
然后,您可以使用一个方法将缓存名称添加到集合中,该方法将在缓存过期后删除缓存名称,在本例中为1秒。我知道这似乎是一段相当多的代码,但只需几个方法,它就可以成为一个非常优雅的解决方案

ScheduledFuture<String> task = executorService.schedule(new Callable<String>() {
  @Override
  public String call() {
    cacheNames.remove("unique_string");
    return "unique_string";
  }
}, 1, TimeUnit.SECONDS);
cacheNames.put("unique_string", task);
ScheduledFuture task=executorService.schedule(new Callable()){
@凌驾
公共字符串调用(){
cacheNames.remove(“唯一字符串”);
返回“唯一_字符串”;
}
},1,时间单位。秒);
cacheNames.put(“唯一字符串”,任务);

为什么不存储钥匙在地图中被列入黑名单的时间(如提示)

大概是这样的:

private final Map<String, Long> _blacklist = new LinkedHashMap<String, Long>() {
   @Override
   protected boolean removeEldestEntry(Map.Entry<String, Long> eldest) {
      return size() > 1000;
   }
};

public boolean isBlacklisted(String key, long timeoutMs) {
   synchronized (_blacklist) {
      long now = System.currentTimeMillis();
      Long blacklistUntil = _blacklist.get(key);
      if (blacklistUntil != null && blacklistUntil >= now) {
         // still blacklisted
         return true;
      } else {
         // not blacklisted, or blacklisting has expired 
         _blacklist.put(key, now + timeoutMs);
         return false;
      }
   }
}
private final Map_blacklist=new LinkedHashMap(){
@凌驾
受保护的布尔重构(Map.Entry最早){
返回大小()>1000;
}
};
公共布尔值被列入黑名单(字符串键,长超时){
已同步(_黑名单){
long now=System.currentTimeMillis();
Long blacklistUntil=\u blacklist.get(key);
如果(blacklistUntil!=null&&blacklistUntil>=now){
//仍被列入黑名单
返回true;
}否则{
//未列入黑名单,或黑名单已过期
_黑名单。put(键,现在+超时);
返回false;
}
}
}

为什么不生成完全不重复或在给定时间内不重复的字符串ID。这样,您就不必跟踪以前的字符串ID。对不起,我意识到我应该更清楚这一点。我自己不控制名称生成。例如,我们可以说名称可以是IP地址、邮政地址ss或其他任何东西,我希望使用我要求的机制来节流(即每秒发送不超过1个单位)。与您的问题类似,我希望能为您的解决方案提供帮助。抱歉,Peter,但正如我在上面所评论的,我自己不控制名称生成,因此无法更改该部分。时间条件不严格。它应该是“大约”“1秒钟。实际上,在这种情况下,我的严格要求是,在下一次尝试使用相同的唯一名称之前,至少必须经过一秒钟,因此如果实现可以使其关闭20-200毫秒,我可以很好地将该值配置为1.2秒,这不是问题。关于离散或按调用缓存失效,我没有真正的要求,如果它解决了我的问题,因为我可以每秒进行50-100次调用,虽然内存管理可能是一个问题,我想如果没有妥善处理?谢谢你的建议,看起来很有帮助!实际上,当仔细观察时,我可以看到这个方法已经被弃用,取而代之的是beta版。我现在将对此进行研究,并将其与@Shawn建议的解决方案进行比较。我正在寻找的是一种“开火并忘记”的解决方案,它不必让我根据我打算做的调用次数来考虑内存管理,基于此,你会说这两种解决方案中的任何一种都有优势吗?@Nils我查看了我公司正在使用的guava版本,但guava进展得太快了。我将更新答案,以免迫使未来的读者走很远的路;-)@关于使用S的手写解决方案的Nils