如何运行后台线程来定期清理列表中的某些元素?
我目前正在实施缓存。 我已完成基本实现,如下所示。 我想要做的是运行一个线程,删除满足特定条件的条目。
class Cache { int timeLimit = 10; //how long each entry needs to be kept after accessed(marked) int maxEntries = 10; //maximum number of Entries HashSet set = new HashSet(); public void add(Entry t){ .... } public Entry access(String key){ //mark Entry that it has been used //Since it has been marked, background thread should remove this entry after timeLimit seconds. return set.get(key); } .... }
我的问题是,我应该如何实现后台线程,以便线程绕过集合中的条目并删除已marked && (last access time - now)>timeLimit
?
编辑
上面只是代码的简化版本,我没有写同步语句。
你为什么重新发明轮子? EhCache (以及任何体面的缓存实现)都会为您完成此任务。 也更轻巧 来自Guava的 MapMaker
Cache
可以自动删除旧条目。
如果你真的想自己实现它,那就不是那么简单了。
-
记住同步。 您应该使用
ConcurrentHashMap
或synchronized
关键字来存储条目。 这可能非常棘手。 -
你必须以某种方式存储每个条目的最后访问时间。 每次访问条目时,都必须更新该时间戳。
-
想想驱逐政策。 如果缓存中有多个
maxEntries
,首先要删除哪些? -
你真的需要一个后台线程吗?
这是令人惊讶的,但EhCache(企业就绪且经过validation)不使用后台线程来使旧条目无效)。 相反,它等待地图填满并懒惰地删除条目。 这看起来是一个很好的权衡,因为线程很昂贵。
-
如果你有一个后台线程,那么每个缓存还是一个全局? 您是在创建新缓存时启动新线程还是拥有所有缓存的全局列表? 这比你想象的要难……
一旦你回答了所有这些问题,实现就相当简单了:每隔一秒左右查看所有条目,如果满足你已编写的条件,则删除条目。
我亲自使用Guava的Cache类型。 它已经是线程安全的,并且内置了一些方法,可以根据时间限制从缓存中逐出。 如果你想要一个线程定期扫描它,你可以这样做:
new Thread(new Runnable() { public void run() { cache.cleanUp(); try { Thread.sleep(MY_SLEEP_DURATION); } catch (Exception e) {}; } }).start();
我不认为你真的需要一个后台线程。 相反,您可以在执行查找之前或之后删除过期的条目。 这简化了整个实现,很难区分。
顺便说一句:如果您使用LinkedHashMap,可以通过覆盖removeEldestEntry将其用作LRU缓存(有关示例,请参阅其javadoc)
首先,你呈现的代码是不完整的,因为HashSet
上没有get(key)
(所以我假设你的意思是某种Map
)而你的代码没有提到任何“标记”。 还有很多方法可以进行缓存,如果不知道要缓存的内容以及原因,很难找出最佳解决方案。
在实现缓存时,通常假设数据结构将由多个线程同时访问。 因此,您需要做的第一件事是使用线程安全的后备数据结构。 HashMap
不是线程安全的,但是ConcurrentHashMap
是。 还有许多其他并发Map
实现,即Guava , Javolution和高级lib 。 除了地图之外,还有其他方法可以构建缓存,它们的用处取决于您的用例。 无论如何,即使您决定不需要后台线程,而且在尝试从缓存中检索过期对象时,也很可能需要使后备数据结构成为线程安全的。 或者让GC使用SoftReference
删除条目。
一旦你的缓存线程安全的内部,你可以简单地启动一个新的(很可能是daemonized)线程,定期扫描/迭代缓存并删除旧条目。 线程将在循环中执行此操作(直到被中断,如果您希望能够再次停止它),然后在每次扫描后hibernate一段时间。
但是,您应该考虑是否值得为您构建自己的缓存实现。 编写线程安全的代码并不容易,我建议您在努力编写自己的缓存实现之前先研究它。 我可以推荐Java Concurrency in Practice一书。
当然,更简单的方法是使用现有的缓存实现。 Java-land中有许多选项,都有自己独特的权衡取舍。
- EhCache和JCS都是通用缓存,适合大多数缓存需求,可以在典型的“企业”应用程序中找到。
- Infinispan是一种针对分布式使用进行了优化的缓存,因此可以缓存比单个机器上的数据更多的数据。 我也喜欢它的基于
ConcurrentMap
的API。 - 正如其他人所提到的,Googles Guava库有一个Cache API,对于小型内存缓存非常有用。
由于您希望限制缓存中的条目数,因此您可能对对象池而不是缓存感兴趣。
- Apache Commons-Pool被广泛使用,其API类似于您自己构建的API。
- 另一方面, Stormpot有一个相当不同的API,我几乎只提到它,因为我写了它。 它可能不是你想要的,但谁能确定你不知道你想要缓存什么,为什么?
首先,要synchronized
或使用对集合的访问权限 基于ConcurrentHashSet
ConcurrentHashMap
的Set
如下面的注释所示。
其次,编写新线程,并将其实现为无限循环,定期迭代先前的集合并删除元素。 您应该以在构造函数中使用正确的集合初始化它的方式编写此类,这样您就不必担心“如何访问正确的集合”。