我正在使用Guava LoadingCache
将一些数据填充到其中,我想每1分钟从LoadingCache
中删除所有条目。
public class MetricHolder {
private final ExecutorService executor = Executors.newFixedThreadPool(2);
private final LoadingCache<String, AtomicLongMap<String>> clientIdMetricCounterCache =
CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES)
.removalListener(RemovalListeners.asynchronous(new SendToDatabase(), executor))
.build(new CacheLoader<String, AtomicLongMap<String>>() {
@Override
public AtomicLongMap<String> load(String key) throws Exception {
return AtomicLongMap.create();
}
});
private static class Holder {
private static final MetricHolder INSTANCE = new MetricHolder();
}
public static MetricHolder getInstance() {
return Holder.INSTANCE;
}
private MetricHolder() {}
public void increment(String clientId, String name) throws ExecutionException {
clientIdMetricCounterCache.get(clientId).incrementAndGet(name);
}
public LoadingCache<String, AtomicLongMap<String>> getClientIdMetricCounterCache() {
return clientIdMetricCounterCache;
}
private static class SendToDatabase implements RemovalListener<String, AtomicLongMap<String>> {
@Override
public void onRemoval(RemovalNotification<String, AtomicLongMap<String>> notification) {
String key = notification.getKey();
AtomicLongMap<String> value = notification.getValue();
System.out.println(key);
System.out.println(value);
// sending these key/value to some other system
}
}
}
我以多线程方式从代码中的许多不同位置调用increment
方法。因此,在1分钟的时间内,它将填充clientIdMetricCounterCache
中的许多指标。现在,我想在每1分钟后删除所有这些指标可靠,并将所有这些指标发送到数据库。
在我的情况下,有时写入increment
方法可能会非常慢但我仍然希望每1分钟删除所有这些条目,而我在这个缓存上根本没有读取任何内容,只是写入它并且然后通过发送到其他系统删除这些记录。以下是我在番石榴wiki
使用CacheBuilder构建的缓存不执行清理并逐出值 “自动”,或在值到期后立即或任何其他内容 那种。相反,它在执行期间执行少量维护 写入操作,或者在写入时偶尔进行读取操作 罕见的。
那么expireAfterWrite
如何运作?它是否像调度程序一样运行,每隔1分钟运行一次并删除clientIdMetricCounterCache
中的所有条目,然后它将在1分钟后再次唤醒并从同一缓存中删除所有条目并继续这样做?阅读维基后,我怀疑它是那样的。如果没有,那么我怎样才能每隔1分钟可靠地丢弃这些记录并发送到其他系统,因为我的写入在一段时间内很少见?
看起来我可能必须使用Guava TimeLimiter
界面和SimpleTimeLimiter
或可能ScheduledExecutorService
来可靠地超时,然后删除条目?如果是的话,任何人都可以提供一个示例,在当前示例中这将如何工作?
答案 0 :(得分:2)
对我来说,看起来你正在滥用缓存,而地图就是这样做的。您没有使用过期,没有大小限制,没有缓存,您只是收集统计信息。
关于您正在使用的唯一功能是加载方面,这并不值得。
我建议改为使用AtomicReference<ConcurrentHashMap<String, AtomicLongMap>>
:
AtomicReference::get
获得当前分钟的版本。clientId
,在AtomicLongMap
中查找ConcurrentHashMap
,如果找不到则创建一个新的putIfAbsent
在Java 7上使用computeIfAbsent
或name
在Java 8上。AtomicLongMap
,您就像发布的那样更新AtomicReference::getAndSet
。getAndSet
替换所有内容。通过替换,您可以确定您的统计信息不会干扰,但是,您应该在volatile
之后稍等一下,因为可能有线程刚刚获得参考并且即将写入。
它会产生比原始方法更多的垃圾,但所有垃圾都会短暂存在,所以你可能会让GC更加快乐。
它很简单,不需要深入了解库或其实现细节。
我猜,AtomicReference
代替{{1}}也可以。