成员变量的hashCode()值不同

时间:2010-05-09 10:27:46

标签: java multithreading

有一段看起来像这样的代码。问题是在启动期间,会发生2次初始化。 (1)某种方法对ForumRepository&执行newInstance()纯粹是为了调用#setCacheEngine。 (2)后面的另一种方法调用#start()。我注意到#cache成员变量的hashCode在某些奇怪的场景中有时会有所不同。由于只有1段代码调用#setCacheEngine,hashCode如何在运行时更改(我假设不同的实例将具有不同的hashCode)。这个地方有错误吗?

public class ForumRepository implements Cacheable
{
    private static CacheEngine cache;
    private static ForumRepository instance;

    public void setCacheEngine(CacheEngine engine) { cache = engine; }

    public synchronized static void start()
    {
        instance = new ForumRepository();
    }

    public synchronized static void addForum( ... )
    {
        cache.add( .. );
        System.out.println( cache.hashCode() );
        // snipped
    }

    public synchronized static void getForum( ... )
    {
        ... cache.get( .. );
        System.out.println( cache.hashCode() );
        // snipped
    }
}

整个系统已接通电线。在servlet的init方法中初始化。 init()方法在概念上看起来像这样:

// create an instance of the DefaultCacheEngine
cache = (CacheEngine)Class.forName( "com..DefaultCacheEngine" ).newInstance();
cache.init();

// init the ForumRepository's static member
Object o = Class.forName( "com.jforum....ForumRepository" ).newInstance();     
if( o instanceof Cacheable )
    ((Cacheable)o).setCacheEngine(cache);

// Now start the ForumRepository
ForumRepository.start();

更新我没有写这段代码。它取自jforum

更新2 找到解决方案。我在下面添加了一个单独的评论来描述问题的原因。谢谢大家。

5 个答案:

答案 0 :(得分:1)

您将不得不提供更多信息,但CacheEngine可能是一种可变数据类型,更糟糕的是,它甚至可能被其他人共享。根据{{​​1}}定义其CacheEngine的方式,这很可能会导致hashCode()aForumRepository看到各种不同的哈希码。

对于同一个对象,如果它是可变的,在一段时间内改变它的cache,只要它以一致的方式完成(完全是另一个主题),这是完全正常的。

另见


hashCode()cache

更多信息已经重新出现,我们现在知道有问题的对象虽然可变,却不会static。但是,在@Override hashCode() cache staticForumRepository字段的设计中,似乎存在严重的问题,其中非static“ setter“setCacheEngine(看起来由Cacheable指定。)

这意味着,无论创建了多少个cache实例,都只有ForumRepository的化身!在某种程度上, <{strong> ForumRepository 的所有实例都“共享”相同的 cache

  

JLS 8.3.1.1 static Fields

     

如果一个字段被声明为static,那么无论该类最终可以创建多少个实例(可能为零),都只存在该字段的一个化身。一个static字段,有时称为类变量,在初始化类时实现。

我认为现在退后一步并提出这些问题非常重要:

  • cache需要static吗?这是有意的吗?
    • ForumRepository的实例是否有自己的cache
    • ...或者他们是否应该“共享”相同的cache
  • 将创建多少ForumRepository个实例?
    • 将设计模式的利弊放在一边,ForumRepository应该是单身吗?
  • setCacheEngine个对象上可以ForumRepository次调用多少次?
    • 如果不止一次召唤IllegalStateException投掷setCacheEngine的防御机制会受益吗?

最佳建议取决于上述问题的答案。第三个要点是可以立即采取行动的,并且会显示是否多次调用ForumRepository。即使它们仅针对每个cache实例调用一次,它仍然在当前事态中实际上是多个“集合”,因为只有一个static

具有非static setter的{{1}}字段是一项需要彻底重新审核的设计决策。

答案 1 :(得分:0)

您确定要比较的ForumRepository类是否相同。如果你正在做newInstance,你可能会遇到一个奇怪的类加载器问题。

答案 2 :(得分:0)

基于可变状态的hashCode实现的可变对象几乎总是一个坏主意。例如,它们在基于散列的集合中表现得非常奇怪 - 如果将这样的对象插入HashSet然后对其进行变异,则contains方法将无法再找到它。 / p>

根据其身份自然区分的对象不应覆盖equalshashCode。如果根据状态覆盖hashCode,则该状态应为不可变状态。示例是String和拳击类型。这些通常被称为“价值类”,因为它们的身份没有意义 - 区分new Integer(42)的多个实例是毫无意义的。

答案 3 :(得分:0)

令我困惑的是这个问题:

  

为什么要查看CacheEngine实例的哈希码?

您的代码似乎将Forum个对象放入缓存中并将其取回。因此,查看Forum实例的哈希码值是有意义的。但是缓存本身的哈希码看起来完全不相关。

话虽如此,如果DefaultCacheEngine类从hashcode继承了java.lang.Object的实现,那么该方法返回的值就是“身份”哈希码,这不能改变对象的生命周期。如果它确实发生了变化,这意味着您现在正在查看DefaultCacheEngine类的不同实例

答案 4 :(得分:0)

我已经解决了我的问题,我想与你分享我学到或发现的东西。

错误的根本原因

jforum.war webapp由Tomcat 6.x通过两个不同的虚拟主机加载两次。所以是的,CacheEngine显示了两个不同的hashCodes,因为它们被加载到单独的webapp类加载器中。

<强>解决方案

我的快速解决方法是通过一个特定的虚拟主机地址限制jforum.war中servlet的调用。