自*首次*插入以来的一段时间后,Java中是否有一个使元素过期的过期映射? [英] Is there an expiring map in Java that expires elements after a period of time since *first* insertion?

查看:165
本文介绍了自*首次*插入以来的一段时间后,Java中是否有一个使元素过期的过期映射?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试查看缓存机制,例如Guava的Cache.它们的有效期仅自上次更新以来.

我正在寻找的是一种数据结构,该结构存储键并在自第一次插入以来经过一段时间后清理键.我正在计划将其值作为对价.

一个场景可能是一个沉默寡言的工作人员,它第一次做一些工作,但是在一段时间内保持沉默-即使请求工作也是如此.如果到期后要求工作,他会做.

知道这种数据结构吗?谢谢.

解决方案

有一些选择.

被动删除

如果不需要在过期或按设定的时间间隔清除过期的密钥(即,在密钥过期或在设定的间隔时不需要删除密钥),则 PassiveExpiringMap 是一个不错的选择.尝试访问此映射中的密钥时,将检查密钥的生存时间(TTL)(所有密钥都具有相同的TTL),如果密钥已过期,则将其从映射中删除并返回null.此数据结构没有有效的清除机制,因此,仅在与键对应的TTL过期之后才访问过期条目时,才删除过期条目.

缓存

如果需要更多基于缓存的功能(例如最大缓存容量和添加/删除侦听),则Google Guava提供装饰器Map接口的某些其他实现):

public class ExpiringKey<T> {

    private final T key;
    private final long expirationTimestamp;

    public ExpiringKey(T key, long ttlInMillis) {
        this.key = key;
        expirationTimestamp = System.currentTimeMillis() + ttlInMillis;
    }

    public T getKey() {
        return key;
    }

    public boolean isExpired() {
        return System.currentTimeMillis() > expirationTimestamp;
    }
}

现在,映射的类型将为Map<ExpiringKey<K>, V>,并带有一些特定的KV类型值.可以使用类似于以下内容的Runnable来表示后台线程:

public class ExpiredKeyRemover implements Runnable {

    private final Map<ExpiringKey<?>, ?> map;

    public ExpiredKeyRemover(Map<ExpiringKey<?>, ?> map) {
        this.map = map;
    }

    @Override
    public void run() {
        Iterator<ExpiringKey<?>> it = map.keySet().iterator();
        while (it.hasNext()) {
            if (it.next().isExpired()) {
                it.remove();
            }
        }
    }
}

然后可以启动Runnable,以便使用ScheduledExecutorService按固定间隔执行,如下所示(每5秒钟将清理一次地图):

Map<ExpiringKey<K>, V> myMap = // ...

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(new ExpiredKeyRemover(myMap), 0, 5, TimeUnit.SECONDS);

请注意,用于myMapMap实现必须同步或允许并发访问,这一点很重要.并发Map实现的挑战在于,ExpiredKeyRemover可能会看到与客户端不同的地图视图,并且如果清理线程为,过期的密钥可能会返回给客户端.尚未完成删除其他键的操作(即使它已删除了所需的/过期的键,因为它的更改可能尚未提交).另外,上述密钥删除代码可以使用流来实现,但是上面的代码仅用于说明逻辑,而不是提供高性能的实现.

希望有帮助.

I tried looking at cache mechanisms, such as Guava's Cache. Their expiration is only since last update.

What I'm looking for is a data structure that stores keys and cleans the keys after a time has passed since the first insertion. I'm planning for the value to be some counter.

A scenario might be a silent worker that does some work for the first time but keeps silent for an expiry period of time - even if work is requested. If work is requested after the expiry time has passed, he'll do the work.

Know of such a data structure? Thanks.

解决方案

There are a few options for this.

Passive Removal

If it is not a requirement to clean up the expired keys as soon as they expire or at set intervals (i.e. a key does not need to be removed when the key expires or at some set interval), then PassiveExpiringMap from Apache Commons Collections is a good option. When attempting to access a key in this map, the Time to Live (TTL) of the key (all keys have the same TTL) is checked and if the key has expired, it is removed from the map and null is returned. This data structure does not have an active clean-up mechanism, so expired entries are only removed when they are accessed after the TTL corresponding to the key has expired.

Cache

If more cache-based functionality is needed (such as maximum cache capacity and add/remove listening), Google Guava provides the CacheBuilder class. This class is more complex than the Apache Commons alternative, but it also provides much more functionality. The trade-off may be worth it if this intended for more of a cache-based application.

Threaded Removal

If active removal of expired keys is needed, a separate thread can be spawn that is responsible for removing expired keys. Before looking at a possible implementation structure, it should be noted that this approach may be less performant than the above alternatives. Besides the overhead of starting a thread, the thread may cause contention with clients accessing the map. For example, if a client wants to access a key and the clean-up thread is currently removing expired keys, the client will either block (if synchronization is used) or have a different view of the map (which key-value pairs are contained in the map) if some concurrent mechanism is employed.

With that said, using this approach is complicated because it requires that the TTL be stored with the key. One approach is to create an ExpiringKey, such as (each key can have its own TTL; even if all of the keys will end up having the same TTL value, this technique removes the need to create a Map decorator or some other implementation of the Map interface):

public class ExpiringKey<T> {

    private final T key;
    private final long expirationTimestamp;

    public ExpiringKey(T key, long ttlInMillis) {
        this.key = key;
        expirationTimestamp = System.currentTimeMillis() + ttlInMillis;
    }

    public T getKey() {
        return key;
    }

    public boolean isExpired() {
        return System.currentTimeMillis() > expirationTimestamp;
    }
}

Now the type of the map would be Map<ExpiringKey<K>, V> with some specific K and V type values. The background thread can be represented using a Runnable that resembles the following:

public class ExpiredKeyRemover implements Runnable {

    private final Map<ExpiringKey<?>, ?> map;

    public ExpiredKeyRemover(Map<ExpiringKey<?>, ?> map) {
        this.map = map;
    }

    @Override
    public void run() {
        Iterator<ExpiringKey<?>> it = map.keySet().iterator();
        while (it.hasNext()) {
            if (it.next().isExpired()) {
                it.remove();
            }
        }
    }
}

Then the Runnable can be started so that it executes at a fixed interval using a ScheduledExecutorService as follows (which will clean up the map every 5 seconds):

Map<ExpiringKey<K>, V> myMap = // ...

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(new ExpiredKeyRemover(myMap), 0, 5, TimeUnit.SECONDS);

It is important to note that the Map implementation used for myMap must be synchronized or allow for concurrent access. The challenge with a concurrent Map implementation is that the ExpiredKeyRemover may see a different view of the map than a client and an expired key may be returned to the client if the clean-up thread is not completed removing other keys (even if it has removed the desired/expired key since its changes may not have been committed yet). Additionally, the above key-removal code can be implemented using streams, but the above code has been used just to illustrate the logic rather than provide a performant implementation.

Hope that helps.

这篇关于自*首次*插入以来的一段时间后,Java中是否有一个使元素过期的过期映射?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆