有没有一种并发方式,相同的Object.equals(Object)返回false?

时间:2013-08-09 19:12:43

标签: java concurrency equals

这一天让我心烦意乱,所以我正在寻找一个具体的答案。

(拜托,请耐心等待,我不打算制作出漂亮的代码。相反,我正在寻找有嗅觉代码的问题)

想象一下,我们有一个来自班级Foo

的有状态对象
public class Foo {
     public int attribute = 0;

     // hashCode implemented :P
     @Override
     public boolean equals(Object o) {
         if (o instanceof Foo) {
            Foo that = (Foo) o;

            return this.attribute == that.attribute;
         }
         return false;
     }
}

我们在Foo上有一些工人

public class DoomBringer implements Runnable {

    private final Foo foo;

    public DoomBringer(Foo foo) {
        this.foo = foo;
    }
    @Override
    public void run() {
       this.foo.attribute++;
    }
}

另一个只将#equals的结果作为参数传递给它的构造函数。

public class SelfEqualityTestPrinter implements Runnable {

    private final Foo foo;

    public SelfEqualityTestPrinter(Foo foo) {
        this.foo = foo;
    }

    @Override
    public void run() {
        System.out.println(foo.equals(foo));
    }
}

如果我们有并发线程修改相同的false实例,是否有可能在某一天打印Foo

我的猜测是可能的。除非我们这样做,否则我认为equals不会同步。避免这种情况的一种方法是在this == o方法上测试Object#equals(Object o),但是通过比较应该相同的不同实例可能会出现同样的问题。

2 个答案:

答案 0 :(得分:4)

是的,在存在并发修改的情况下,foo.equals(foo)完全有可能返回false。解决这个问题的一种方法是考虑谁阅读和修改内容,并相应地进行同步。

答案 1 :(得分:2)

是的,它可能。任何可以访问这些变量的线程都可以随时更改任何未同步的变量。

如果foo.attribute的访问权限已同步,并且引用.equalsattribute已同步,则情况并非如此。

更令人惊讶的是,如果一个线程更改attribute,另一个线程可以在之后读取它并获得其旧值!最后一篇文章可以通过使用volatile关键字来修复。这是一个很好的explanation of the volatile keyword

正如Brian和yshavit所说,不宣布变量波动的问题包括竞争条件。例如,在这里,调用attribute++的2个线程可能导致attribute仅增加一次。要递增的第二个线程可以使用存储在其缓存内存中的attribute的未递增值,而不是第一个线程写入的递增值。