Java对象锁定等于还是同步等于?(对hashcode也有效)
在调用equals方法期间,建议采用什么方法进行同步,是覆盖equals并添加关键字Java对象锁定等于还是同步等于?(对hashcode也有效),java,multithreading,equals,hashcode,Java,Multithreading,Equals,Hashcode,在调用equals方法期间,建议采用什么方法进行同步,是覆盖equals并添加关键字synchronized,还是在调用equals时获得两个对象上的锁更好 例如,我有: public class EqualsTesterHelper { public int test = 0; @Override public boolean equals(Object obj) { if (!(obj instanceof EqualsTesterHelper)
synchronized
,还是在调用equals时获得两个对象上的锁更好
例如,我有:
public class EqualsTesterHelper {
public int test = 0;
@Override
public boolean equals(Object obj) {
if (!(obj instanceof EqualsTesterHelper)){
return false;
}
EqualsTesterHelper a = (EqualsTesterHelper) obj;
return (a.test == this.test);
}
}
现在,如果我使用多线程测试equals方法,它显然会失败,例如:
public class EqualsTesterMain{
private static EqualsTesterHelper helper1 = new EqualsTesterHelper();
private static EqualsTesterHelper helper2 = new EqualsTesterHelper();
private static Random rand = new Random();
public static void main(String[] args) {
helper1.test = 1;
helper2.test = 1;
System.out.println(helper1.equals(helper2));
ExecutorService executor = Executors.newFixedThreadPool(100);
for (int i = 0; i < 100; i++){
executor.execute(new RunnerTest());
}
}
private static class RunnerTest implements Runnable{
public void run() {
while (true){
modifyHelper(helper1, rand.nextInt(10));
helper1.equals(helper2);
modifyHelper(helper2, rand.nextInt(10));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void modifyHelper(EqualsTesterHelper helper, int newValue){
helper.test = newValue;
}
}
}
还是在我的Runnable中对两个对象都添加锁定更好:
public void run() {
while (true){
modifyHelper(helper1, rand.nextInt(10));
synchronized(helper1){
synchronized(helper2){
helper1.equals(helper2);
}
}
modifyHelper(helper2, rand.nextInt(10));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
我看了一下,但似乎唯一的答案是,您应该确保在计算equals(或hashcode)时不会修改对象,这不会提示哪种解决方案更好
Edit:实际上我意识到我的“synchronized”equals有缺陷,因为我仍然需要在方法中传递的对象上进行同步。同步
equals()
没有意义
假设您有以下代码:
Foobar foobarA = ...;
Foobar foobarB = ...;
if (foobarA.equals(foobarB)) {
doSomethingThatOnlyMakesSenseIfTheyAreEqual(...);
}
如果有其他线程可能会改变两个对象的相等性,那么在调用doSomething…()
时,将synchronized
添加到equals
方法将无法确保对象相等
原因很简单:在equals()
调用返回true
之后,但在doSomething…()
调用之前或期间,其他线程可能会更改关系
如果需要同步,则必须按如下方式进行同步:
Foobar foobarA = ...;
Foobar foobarB = ...;
Object lock = new Object();
synchronized(lock) {
if (foobarA.equals(foobarB)) {
doSomethingThatOnlyMakesSenseIfTheyAreEqual(...);
}
}
当然,修改对象的代码也必须在同一个
锁上同步同步equals()
没有意义
假设您有以下代码:
Foobar foobarA = ...;
Foobar foobarB = ...;
if (foobarA.equals(foobarB)) {
doSomethingThatOnlyMakesSenseIfTheyAreEqual(...);
}
如果有其他线程可能会改变两个对象的相等性,那么在调用doSomething…()
时,将synchronized
添加到equals
方法将无法确保对象相等
原因很简单:在equals()
调用返回true
之后,但在doSomething…()
调用之前或期间,其他线程可能会更改关系
如果需要同步,则必须按如下方式进行同步:
Foobar foobarA = ...;
Foobar foobarB = ...;
Object lock = new Object();
synchronized(lock) {
if (foobarA.equals(foobarB)) {
doSomethingThatOnlyMakesSenseIfTheyAreEqual(...);
}
}
当然,修改对象的代码也必须在同一个锁上同步
为什么要同步equals
或hashCode
?这些方法的实现是否会更改对象的状态?这取决于…..最好使等于和hashCode
取决于实例的不可变属性,原因不是多线程。例如,如果hashCode
可以更改,您可能无法在HashSet
中找到它。@mike:当equals计算可变字段时,该字段可以由并发线程更改。@asettouf在这种情况下,您应该使用外部同步,正如Javadoc中所述,为什么要同步等于
或hashCode
?这些方法的实现是否会更改对象的状态?这取决于…..最好使等于和hashCode
取决于实例的不可变属性,原因不是多线程。例如,如果hashCode
可以更改,您可能无法在HashSet
中找到该字段。@mike:当equals计算可变字段时,该字段可以由并发线程更改。@asettouf在这种情况下,您应该使用外部同步,如Javadoc中所述。您有一点,虽然我会假设,如果使用equals方法在测试中的某个时刻测试相等性,您可能不需要“做一些有意义的事情”@asettouf当有多个线程在不同步的情况下运行时,您如何定义“某个时刻”?换句话说,如果线程A知道两个对象在其过去的某个时刻是相等的,但它永远无法知道它们现在是否相等,那么线程A知道这两个对象是相等的有什么用呢?不要忘记,非同步线程不一定会看到事情以相同的顺序发生。如果无法知道它们在其他线程的过去是否相等,那么知道它们在线程A过去的某个时间点相等又有什么意义呢?假设您有一个对象,您从HTTP请求创建了第二个对象,该对象应与第一个对象相等,您定义了10秒的超时,然后检查两者是否相等。在equals调用过程中,如果对象不相等,在“10秒+delta t”时,您可以创建对象,equals仍然返回true,尽管10秒的超时已经过去(很公平,这显然是一个边缘情况,这是我的问题),同时使equals
同步可以防止修改错误(即,如果您的equals
方法检查某个内部值是否为null,如果不是null,则该方法基于值不为null的信念进行操作,其他线程将内部状态更改为null可能会抛出难以调试的NullPointerException
)…你说得有道理,不过我想如果你的equals方法用于测试某个时刻的相等性,你可能不需要“做一些有意义的事情”@asettouf你如何定义“某个时刻”当有多个线程在没有同步的情况下运行时?换句话说,如果线程A知道两个对象在过去的某个时刻是相等的,如果它永远不知道它们现在是否相等,那么线程A又有什么用呢?别忘了,没有同步的线程不一定看到事情以相同的顺序发生。这有什么意义在知道它们在线程A的过去的某个点上是相等的,如果没有办法知道它们在其他线程的过去是否是相等的?假设你有一个对象,你从一个HTTP请求创建了第二个对象