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)
尝试同步equals和hashCode在对象内部将无法正常工作.考虑的情况下HashMap使用hashCode,发现其中"斗"的对象将是,然后使用equals依次搜索桶中的所有对象.
如果允许对象以改变结果的方式进行变异,hashCode或者equals最终会出现HashMap调用的情况hashCode.它获取锁,获取哈希并再次释放锁. HashMap然后继续计算要使用的"桶".但是之前HashMap可以获得对等号的锁定,其他人抓住锁定并改变对象,使其equals与之前的值不一致hashCode.这将导致灾难性的后果.
将hashCode在很多地方使用的方法和equals和是核心Java集合API.重新考虑不需要同步访问这些方法的应用程序结构,我可能很有价值.或者至少不同步对象本身.
为什么要同步?如果在比较期间它们发生了变化,那么它是否重要的用例是什么?如果在依赖于相等性的代码之前立即进行更改并且无关紧要.(即如果你有代码取决于equulity,如果在此代码之前或期间值变得不相等会发生什么)
我想你必须看看更大的进程,看看你需要锁定的位置.
如果在保持同步后结果不能保证为真,则同步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)
不可否认,你仍然会遇到同样的问题,但至少你在正确的点上进行同步;)
哈希数据结构等各种事物都需要正确实现equals()和hashCode(),因此您没有真正的选择。从另一个角度来看,equals()和hashCode()也只是方法,与其他方法对同步的要求是一样的。equals()您仍然遇到死锁问题,但它并不特定于导致死锁的事实。