views:

799

answers:

6

While discussing a Java synchronization question, someone made a comment that the following snippets are not equivalent (and may compile to different bytecodes):

public synchronized void someMethod() {
  //stuff
}

and

public void someMethod() {
  synchronized (this) {
    //stuff
  }
}

Are they equivalent?

A: 

I can't see any functional difference - both synchronize their entire method bodies on (this). How did the person who commented that these are different justify their statement?

Steve B.
A: 

Yes. Using the synchronized keyword on an instance method uses 'this' as a monitor ,also using it on a class method (static method) uses the class' Class object (Foo.class).

This way you can synchronize entire methods, and in the same time, synchronize it with a code snippet in another method using the synchronized-block style.

abyx
You are correct that if the class method were static then the lock would be on the class object rather than the instance object. However that is not the case here, it's an instance method.
Steve B.
Steve B.: Exactly what I've said...
abyx
+6  A: 

I made the original comment that the statements are identical.

In both cases, the first thing that happens is that the calling thread will try to acquire the current object's (meaning, this') monitor.

I don't know about different bytecode, I'll be happy to hear the difference. But in practice, they are 100% identical.

EDIT: i'm going to clarify this as some people here got it wrong. Consider:

public class A {
    public synchronized void doStuff()
    {
        // do stuff
    }
}

public class B extends A {
    public void doStuff()
    {
        // do stuff
        // THIS IS OVERRIDE!
    }
}

In this case doStuff() in class B still overrides doStuff() in class A even though it is not synchronized.

Synchronized keyword is never part of the contract! Not for subclasses, not for interfaces, not for abstract classes.

Yuval A
So was it Eljenso's mistake to say that you said they're NOT equivalent?
Rob Kennedy
No, there is someone claiming they are not equivalent... I myself do not understand how he got to that conclusion :)
Yuval A
I don't know quite what the controversy is here. They DO compile to different things. That means they're not 100% identical.
Neil Coffey
+12  A: 

They are equivalent in function, though the compilers I tested (Java 1.6.0_07 and Eclipse 3.4) generate different bytecode. The first generates:

// access flags 33
public synchronized someMethod()V
  RETURN

The second generates:

// access flags 1
public someMethod()V
  ALOAD 0
  DUP
  MONITORENTER
  MONITOREXIT
  RETURN

(Thanks to ASM for the bytecode printing).

So the difference between them persists to the bytecode level, and it's up to the JVM to make their behavior the same. However, they do have the same functional effect - see the example in the Java Language Specification.

It should be noted, that if the method is overridden in a subclass, that it is not necessarily synchronized - so there is no difference in that respect either.

I also ran a test to block a thread trying access the monitor in each case to compare what their stack traces would look like in a thread dump, and they both contained the method in question, so there is no difference there either.

Dave L.
I appreciate the correction :) and deleted my wrong answer. I thought synchronized was part of the method contract. See http://www.javapractices.com/topic/TopicAction.do?Id=67
Alex B
A: 

I made the original comment. My comment was that they are logically equivalent, but compile to different bytecode.

I didn't add anything else to justify it at the time because there's not much to justify really-- they just do compile to different bytecode. If you declare a method as synchronized, then that synchronization is part of the method's definition. A synchronized block within a method isn't part of the method's definition, but instead involves separate bytecodes to acquire and release the monitor, as one of the posters above has illustrated. Strictly speaking, they're slightly different things, though to the overall logic of your program, they're equivalent.

When does this matter? Well, on most modern desktop VMs, hardly ever. But for example:

  • a VM could in principle make optimisations in one case but not the other
  • there are some JIT compiler optimisations where the number of bytecodes in the method is taken as a criterion for what optimisations to make
  • a VM without a JIT compiler (admittedly few nowadays, but maybe on an older mobile device?) will have more bytecodes to process on each call
Neil Coffey
synchronized on a method is not part of the method signature.
Steve Kuo
I think you're twisting my words. As I said, it is part of that method's DEFINITION. The compiler sets the ACC_SYNCHRONIZED flag on that method's header to dictate to the VM that it should acquire the monitor while calling that method.
Neil Coffey
+1 good answer, point well made that it is "logically equivalent", nothing said about method signature.
eljenso
A: 

MyObject myObjectA; MyObject myObjectB;

public void someMethod() { synchronized (this) { //stuff } }

public void someMethodA() { synchronized (myObjectA) { //stuff } }

public void someMethodB() { synchronized (myObjectB) { //stuff } }

in this case, someMethod blocks entire class, someMethodA blocks myObjectA only, someMethodB blocks myObjectB only. someMethodA and someMethodB can be invokded at the same time.