gpt4 book ai didi

java - Guava 缓存api中的RemovalListener回调是否确保没有人在使用该对象

转载 作者:搜寻专家 更新时间:2023-11-01 01:45:38 27 4
gpt4 key购买 nike

我在 wiki page 中阅读了有关缓存的代码示例/文档.我看到回调 RemovalListener 可用于拆除已逐出的缓存对象等。我的问题是,在调用提供的 RemovalListener 之前,库是否确保该对象未被任何其他线程使用。让我们考虑文档中的代码示例:

CacheLoader<Key, DatabaseConnection> loader = 
new CacheLoader<Key, DatabaseConnection> () {
public DatabaseConnection load(Key key) throws Exception {
return openConnection(key);
}
};
RemovalListener<Key, DatabaseConnection> removalListener =
new RemovalListener<Key, DatabaseConnection>() {
public void onRemoval(RemovalNotification<Key, DatabaseConnection> removal) {
DatabaseConnection conn = removal.getValue();
conn.close(); // tear down properly
}
};

return CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.MINUTES)
.removalListener(removalListener)
.build(loader);

这里缓存被配置为在创建后 2 分钟驱逐元素(我知道这可能不是精确的两分钟,因为驱逐会与用户读/写调用等一起搭载)但是无论什么时候,图书馆会检查是否没有对传递给 RemovalListener 的对象的 Activity 引用?因为我可能有另一个线程很久以前就从缓存中获取了对象,但可能仍在使用它。在那种情况下,我无法从 RemovalListener 对其调用 close()

RemovalNotification 的文档还说:删除单个条目的通知。如果键和/或值已经被垃圾回收,则它们可能为空。所以根据它 conn 在上面的例子中可以是 null。在这种情况下,我们如何正确地拆除 conn 对象?在这种情况下,上面的代码示例也会抛出 NullPointerException

我要解决的用例是:

  1. 缓存元素需要在创建两分钟后过期。
  2. 被逐出的对象需要关闭,但只有在确保没有人使用它们之后。

最佳答案

这里是 Guava 贡献者。

My question is does the library make sure that the object is not being used by any other thread before calling the provided RemovalListener.

不,这对 Guava 通常来说是不可能的——而且无论如何这都是个坏主意!如果缓存值为 Integer s,那么因为Integer.valueOf重复使用 Integer对于低于 128 的整数对象,您永远不会使值低于 128 的条目过期。那将是不好的

Also the documentation of RemovalNotification says that: A notification of the removal of a single entry. The key and/or value may be null if they were already garbage collected. So according to it conn could be null in the above example.

明确地说,这只有在您使用 weakKeys 时才有可能, weakValues , 或 softValues . (而且,正如您已经正确推断的那样,如果您需要对值进行一些拆解,就不能真正使用其中任何一个。)如果您只使用其他形式的过期,您将永远不会得到 null键或值。

一般来说,我认为基于 GC 的解决方案在这里行不通。您必须对连接有强引用才能正确关闭它。 (重写 finalize() 在这里可能会起作用,但通常这确实是一件坏事。)

相反,我的方法是缓存对某种包装器的引用。有点像

 class ConnectionWrapper {
private Connection connection;
private int users = 0;
private boolean expiredFromCache = false;
public Connection acquire() { users++; return connection; }
public void release() {
users--;
if (users == 0 && expiredFromCache) {
// The cache expired this connection.
// We're the only ones still holding on to it.
}
}
synchronized void tearDown() {
connection.tearDown();
connection = null; // disable myself
}

}

然后使用 Cache<Key, ConnectionWrapper>RemovalListener看起来像……

 new RemovalListener<Key, ConnectionWrapper>() {
public void onRemoval(RemovalNotification<Key, ConnectionWrapper> notification) {
ConnectionWrapper wrapper = notification.getValue();
if (wrapper.users == 0) {
// do the teardown ourselves; nobody's using it
wrapper.tearDown();
} else {
// it's still in use; mark it as expired from the cache
wrapper.expiredFromCache = true;
}
}
}

...然后强制用户使用 acquire()release()适本地。

我认为没有比这种方法更好的方法了。检测没有其他对连接的引用的唯一方法是使用 GC 和弱引用,但是如果没有对它的强引用就不能拆除连接——这会破坏整点。你不能保证它是否是 RemovalListener或者需要断开连接的连接用户,因为如果用户花费超过两分钟来完成它的事情怎么办?我认为这可能是唯一可行的方法。

(警告:上面的代码假定一次只有一个线程做事;它根本不是同步的,但希望如果您需要它,那么这足以让您了解它应该如何工作。)

关于java - Guava 缓存api中的RemovalListener回调是否确保没有人在使用该对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11563848/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com