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

发布于 2024-12-06 06:43:49 字数 668 浏览 1 评论 0原文

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

我试图解决的问题是两个线程不应该能够在一定时间内使用相同的名称值(所以这是一种黑名单,确保相同的“名称”,就像消息引用一样,可以在经过一定时间段之前不会被另一个线程重用)。我自己不控制名称生成,因此我无法对实际名称/字符串执行唯一性,它应该被视为一种节流/限制机制,以防止同一名称每秒使用多次。

例子: 线程 #1 执行 cache.add("unique_string, 1) 操作,将名称“unique_string”存储 1 秒。 如果任何线程在 1 秒内通过执行例如 cache.get("unique_string") 来查找“unique_string”,它将获得肯定响应(项目存在),但此后该项目应该过期并且从集合中删除。

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

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

一个简单的想法是有一个 ConcurrentHashMap 对象,其键设置为“name”,值设置为过期时间,然后每秒运行一个线程并删除其值(过期时间)已过的所有元素,但是我不确定这会有多有效?我缺少一个更简单的解决方案吗?

I am looking for a concurrent Set with expiration functionality for a Java 1.5 application. It would be used as a simple way to store / cache names (i.e. String values) that expire after a certain time.

The problem I'm trying to solve is that two threads should not be able to use the same name value within a certain time (so this is sort of a blacklist ensuring the same "name", which is something like a message reference, can't be reused by another thread until a certain time period has passed). I do not control name generation myself, so there's nothing I can do about the actual names / strings to enforce uniqueness, it should rather be seen as a throttling / limiting mechanism to prevent the same name to be used more than once per second.

Example:
Thread #1 does cache.add("unique_string, 1) which stores the name "unique_string" for 1 second.
If any thread is looking for "unique_string" by doing e.g. cache.get("unique_string") within 1 second it will get a positive response (item exists), but after that the item should be expired and removed from the set.

The container would at times handle 50-100 inserts / reads per second.

I have really been looking around at different solutions but am not finding anything that I feel really suites my needs. It feels like an easy problem, but all solutions I find are way too complex or overkill.

A simple idea would be to have a ConcurrentHashMap object with key set to "name" and value to the expiration time then a thread running every second and removing all elements whose value (expiration time) has passed, but I'm not sure how efficient that would be? Is there not a simpler solution I'm missing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

度的依靠╰つ 2024-12-13 06:43:49

Google 的 Guava 库包含这样的缓存: CacheBuilder

Google's Guava library contains exactly such cache: CacheBuilder.

千年*琉璃梦 2024-12-13 06:43:49

如何使用线程执行器创建一个项目过期的 Map

//Declare your Map and executor service
final Map<String, ScheduledFuture<String>> cacheNames = new HashMap<String, ScheduledFuture<String>>();
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

然后您可以使用一种方法将缓存名称添加到您的集合中,该方法将在它过期后将其删除,在本例中为一秒钟。我知道这看起来像是相当多的代码,但只需几个方法就可以成为一个非常优雅的解决方案。

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);

How about creating a Map where the item expires using a thread executor

//Declare your Map and executor service
final Map<String, ScheduledFuture<String>> cacheNames = new HashMap<String, ScheduledFuture<String>>();
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

You can then have a method that adds the cache name to your collection which will remove it after it has expired, in this example its one second. I know it seems like quite a bit of code but it can be quite an elegant solution in just a couple of methods.

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);
感悟人生的甜 2024-12-13 06:43:49

一个简单的、独特的字符串模式,不会重复

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 年。

A simple unique string pattern which doesn't repeat

private static final AtomicLong COUNTER = new AtomicLong(System.currentTimeMillis()*1000);
public static String generateId() {
     return Long.toString(COUNTER.getAndIncrement(), 36);
}

This won't repeat even if you restart your application.

Note: It will repeat after:

  • you restart and you have been generating over one million ids per second.
  • after 293 years. If this is not long enough you can reduce the 1000 to 100 and get 2930 years.
亚希 2024-12-13 06:43:49

这取决于 - 如果您需要严格的时间条件,还是软的(如 1 秒 +/- 20 毫秒)。
另外,如果您需要离散缓存失效或“通过调用”。

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

您还可以查看存储的密钥时间戳并检查它是否已过期。

It depends - If you need strict condition of time, or soft (like 1 sec +/- 20ms).
Also if you need discrete cache invalidation or 'by-call'.

For strict conditions I would suggest to add a distinct thread which will invalidate cache each 20milliseconds.

Also you can have inside the stored key timestamp and check if it's expired or not.

伪装你 2024-12-13 06:43:49

为什么不将密钥列入黑名单的时间存储在地图中(如

像这样的东西:

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;
      }
   }
}

Why not store the time for which the key is blacklisted in the map (as Konoplianko hinted)?

Something like this:

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;
      }
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文