简体   繁体   中英

Java object locks equals or synchronized equals? (valid for hashcode as well)

What is the recommended way to synchronize during a call to the equals method, is it overriding equals and adding the keyword synchronized or is it better to acquire a lock on both objects when calling equals?

For instance, I have:

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);
    }   
}

Now if I use multiple threads testing the equals method, it obviously fails, using for instance:

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;
        }   
    }
}

I obviously get inconsistent result due to data racing.

So would it be better that I modify EqualsTesterHelper equals method to :

@Override
public  synchronized boolean equals(Object obj) {
    synchronized(obj){
        if (!(obj instanceof EqualsTesterHelper)){
            return false;
        }
        EqualsTesterHelper a = (EqualsTesterHelper) obj;
        System.out.println("In equals, a.test: " + a.test );
        System.out.println("In equals, this.test: " + this.test );
        System.out.println("In equals, equals: " + (this.test  == a.test));
        return (a.test == this.test);
    }

}

Or is it better to add locking on both object in my 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();
            }
        }
    }

I had a look at this thread , however it seems the only answer is that you should ensure objects are not modified during the evaluation of equals (or hashcode) which does not hint to which solution would be better.

Edit : Actually I realized that my "synchronized" equals is flawed as I would still need to synchronize on the object passed in the method.

It doesn't make sense to synchronize equals() .

Suppose you have this code:

Foobar foobarA = ...;
Foobar foobarB = ...;
if (foobarA.equals(foobarB)) {
    doSomethingThatOnlyMakesSenseIfTheyAreEqual(...);
}

If there's other threads that potentially could alter the equality of the two objects, then adding synchronized to the equals method will not ensure that the objects are equal when doSomething...() gets called.

The reason is simple: The other thread(s) could change the relationship after the equals() call returns true , but before or during the doSomething...() call.

If it needs synchronization, then you'd have to synchronize it like this:

Foobar foobarA = ...;
Foobar foobarB = ...;
Object lock = new Object();

synchronized(lock) {
    if (foobarA.equals(foobarB)) {
        doSomethingThatOnlyMakesSenseIfTheyAreEqual(...);
    }
}

And, of course, the code that modifies the objects also would have to be synchronized on the same lock .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM