在Java中正确同步equals()

Phi*_*ipp 11 java synchronization equals

我有以下类,只包含一个字段i.对象的锁定("this")保护对该字段的访问.实现equals()时,我需要锁定此实例(a)和另一个(b).如果线程1调用a.equals(b)并且同时线程2调用b.equals(a),则锁定顺序在两个实现中是相反的并且可能导致死锁.

我应该如何为具有同步字段的类实现equals()?

public class Sync {
    // @GuardedBy("this")
    private int i = 0;
    public synchronized int getI() {return i;}
    public synchronized void setI(int i) {this.i = i;}

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        synchronized (this) {
            result = prime * result + i;
        }
        return result;
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Sync other = (Sync) obj;
        synchronized (this) {
            synchronized (other) {
                // May deadlock if "other" calls 
                // equals() on "this" at the same 
                // time 
                if (i != other.i)
                    return false;
            }
        }
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

leo*_*onm 9

尝试同步equalshashCode在对象内部将无法正常工作.考虑的情况下HashMap使用hashCode,发现其中"斗"的对象将是,然后使用equals依次搜索桶中的所有对象.

如果允许对象以改变结果的方式进行变异,hashCode或者equals最终会出现HashMap调用的情况hashCode.它获取锁,获取哈希并再次释放锁. HashMap然后继续计算要使用的"桶".但是之前HashMap可以获得对等号的锁定,其他人抓住锁定并改变对象,使其equals与之前的值不一致hashCode.这将导致灾难性的后果.

hashCode在很多地方使用的方法和equals和是核心Java集合API.重新考虑不需要同步访问这些方法的应用程序结构,我可能很有价值.或者至少不同步对象本身.


use*_*019 7

为什么要同步?如果在比较期间它们发生了变化,那么它是否重要的​​用例是什么?如果在依赖于相等性的代码之前立即进行更改并且无关紧要.(即如果你有代码取决于equulity,如果在此代码之前或期间值变得不相等会发生什么)

我想你必须看看更大的进程,看看你需要锁定的位置.

  • 实际上,问题是可见性问题.如果不使用synchronized,有效排序仍可能导致意外结果(即,应该在之前设置的值不可见). (2认同)

sfu*_*ger 6

如果在保持同步后结果不能保证为真,则同步equals()的点在哪里:

if (o1.equals(o2)) {
  // is o1 still equal to o2?
}
Run Code Online (Sandbox Code Playgroud)

因此,您可以简单地将getI()内部的调用同步一个接一个地调整,而不会更改输出 - 它很简单,不再有效.

您将始终必须同步整个块:

synchronized(o1) {
  synchronized(o2) {
    if (o1.equals(o2)) {
      // is o1 still equal to o2?
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

不可否认,你仍然会遇到同样的问题,但至少你在正确的点上进行同步;)


ska*_*man 0

哈希数据结构等各种事物都需要正确实现equals()hashCode(),因此您没有真正的选择。从另一个角度来看,equals()hashCode()也只是方法,与其他方法对同步的要求是一样的。equals()您仍然遇到死锁问题,但它并不特定于导致死锁的事实。