Lately I was wondering what’s the difference between synchronized method and block in Java. Assuming you’re using sync block monitor on this they work exactly the same. So what’s the difference ? Let’s check what’s beneath the hood.

By the way all is executed and tested on Oracle jdk 1.8.0_101.

Simplest case

Let’s take the easiest example there is.

public synchronized void syncMethod() {
}

public void syncBlock() {
    synchronized (this) {
    }
}

Both those methods will work the same and will be exclusively blocking. Is there any difference in generated bytecode ? Let’s check.

Internals

Bytecode generated from this simple case looks a little bit surprising. Well maybe not to all but definitely to some it will.

  public synchronized void syncMethod();
    Code:
       0: return

  public void syncBlock();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter
       4: aload_1
       5: monitorexit
       6: goto          14
       9: astore_2
      10: aload_1
      11: monitorexit
      12: aload_2
      13: athrow
      14: return
    Exception table:
       from    to  target type
           4     6     9   any
           9    12     9   any

Sync method looks like it does nothing at all. Sync block even generated exception throwing. So let’s now look at verbose bytcode.

  public synchronized void syncMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 28: 0

  public void syncBlock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: aload_1
         5: monitorexit
         6: goto          14
         9: astore_2
        10: aload_1
        11: monitorexit
        12: aload_2
        13: athrow
        14: return
      Exception table:
         from    to  target type
             4     6     9   any
             9    12     9   any
      LineNumberTable:
        line 31: 0
        line 34: 4
        line 35: 14
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 9
          locals = [ class pl/spc/sync/Main, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

Now there’s at least additional method flag for sync method. Let’s see what JVM spec says about magical ACC_SYNCHRONIZED flag.

For code written in the Java programming language, perhaps the most common form of synchronization is the synchronized method. A synchronized method is not normally implemented using monitorenter and monitorexit. Rather, it is simply distinguished in the run-time constant pool by the ACC_SYNCHRONIZED flag, which is checked by the method invocation instructions.

Simply it means that JIT compiler will handle syncing for us and it won’t use monitorenter and monitorexit opcodes.

Pros of such solution is that we don’t waste any method bytecodes for synchronization. But what about performance ?

Performance

Let’s change code a little bit so it won’t be empty

public int tmp = 0;

public synchronized void syncMethod() {
    tmp++;
}

public void syncBlock() {
    synchronized (this) {
        tmp++;
    }
}

and write some simple test.

@RunWith(Parameterized.class)
public class SyncTest {
    @Parameterized.Parameters
    public static Collection<Object> data() {
        return Arrays.asList(new Object[10]);
    }

    @Parameter
    public Object param;

    private static long blockCumulativeTime = 0;
    private long blockTestExecTime = 0;
    private static long methodCumulativeTime = 0;
    private long methodTestExecTime = 0;

    @After
    public void count() {
        blockCumulativeTime += blockTestExecTime;
        methodCumulativeTime += methodTestExecTime;
    }

    @AfterClass
    public static void syso() {
        System.out.println("block: " + blockCumulativeTime);
        System.out.println("method: " + methodCumulativeTime);
    }

    @Test
    public void syncBlockPerfTest() {
        final SyncMethods main = new SyncMethods();

        IntStream.range(0, 1000000).forEach((i) -> main.syncBlock());

        long start = System.currentTimeMillis();

        IntStream.range(0, 1000000).forEach((i) -> main.syncBlock());

        blockTestExecTime = System.currentTimeMillis() - start;
    }

    @Test
    public void syncMethodPerfTest() {
        final SyncMethods main = new SyncMethods();

        IntStream.range(0, 1000000).forEach((i) -> main.syncMethod());

        long start = System.currentTimeMillis();

        IntStream.range(0, 1000000).forEach((i) -> main.syncMethod());

        methodTestExecTime = System.currentTimeMillis() - start;
    }
}

On average sync block is slower by 10-12%.

Summary

From what I tested I think that it’s better to use synchronized method instead of block if we want to synchronize on this.