1

我最近开始玩 LoadingCache 类,我得到了一些意想不到的行为。我现在知道解决方案,但我发布了这个问题,以便其他人可以从中学习。

基本上,我在缓存中为某个键放置了一个非空值。键是值在文件中的位置,使用 CacheLoader.load( ) 方法,可以从文件中读取值。目前 LoadingCache 由单个线程使用。

当我运行我的应用程序时,删除通知侦听器被调用,并使用一个以前用于插入非空值的键的空值。我知道这一点是因为我使用断言语句来检查进入缓存的所有值是否不为空(无论如何都不应该发生这种情况)。我也可以看到这一点,因为我在 CacheLoader.load() 调用的方法中打印了密钥:CacheLoader.load() 是唯一可以调用打印密钥的方法的方法。此外,此方法不能返回空值。

如果有人可以让我知道我做错了什么,我将非常感激。

以下是我对加载缓存的定义。

    private LoadingCache<Long,T> gcache( ) {
        @SuppressWarnings( Constants.UNCHECKED )
        final CacheLoader<Long,T> loader =
            new CacheLoader<Long,T>() {
                public T load(Long key) {
                    // This following method _did_ print the key for which
                    // the RemovalNotification listener returns a null value.
                    // See output further on.
                    return getElement( key );
                }
            };
        @SuppressWarnings( Constants.UNCHECKED )
        final RemovalListener<Long,T> listener
            = new RemovalListener<Long,T>( ) {
                  @Override
                  public void onRemoval( RemovalNotification<Long,T> notification ) {
                      final T value = notification.getValue( );
////////////////////////////////////////////////////////////////////////////
if (value == null) {
System.out.println( "????: " + notification.getKey( ) );
}
assert( value != null);
////////////////////////////////////////////////////////////////////////////
                      if (!value.immutable( )) {
                          put( notification.getKey( ), value );
                      }
                  }
              };
        @SuppressWarnings( Constants.UNCHECKED )
        final LoadingCache<Long, T> gcache
            = CacheBuilder.newBuilder( )
                          .softValues( )
                          .removalListener( listener )
                          .build( loader );
        return gcache;
    }

    private T getElement( long pos ) {
        // Critical section, but at the moment there's only one thread.
        enter( );
        seek( pos );
        final T creation = inflator.inflate( this );
///////////////////////////////////////////////////////////////////////
System.out.println( "getElement: pos = " + pos );
assert( creation != null );
///////////////////////////////////////////////////////////////////////
        leave( );
        return creation;
    }

一些输出:

[ cut ]
getElement: pos = 362848
[ cut ]
????: 362848

在问号之后,我得到了无法控制的输出量,并且 JVM 很难停止。我必须按几次键才能终止应用程序。(当然 kill 也可以,但键盘速度稍快一些。)

4

1 回答 1

6

我只是在猜测:在它的 javadoc 中RemovalNotification读取

删除单个条目的通知。如果它们已经被垃圾回收,则键和/或值可能为空。

而你正在使用softValues(). 当值被 GC'ed 时,整个条目将从缓存中删除,并且密钥也可能被收集。

于 2012-08-26T18:12:07.290 回答