Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the synchronization cost of calling a synchronized method from a synchronized method?

Tags:

Is there any difference in performance between this

synchronized void x() {
    y();
}

synchronized void y() {
}

and this

synchronized void x() {
    y();
}

void y() {
}
like image 590
Evgeniy Dorofeev Avatar asked Sep 25 '13 05:09

Evgeniy Dorofeev


People also ask

Why synchronization is expensive?

It is expensive because if you are using threads, and a number of threads have to go through a synchronized section of code, only one of them may be executed at a time. It is like a bottleneck. It is even expensive when you use a single thread, because it has to check anyway if he is allowed to run.

What will happens if a synchronized method is called?

When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

What is synchronization explain synchronized method and synchronized blocks?

synchronized method acquires a lock on the whole object. This means no other thread can use any synchronized method in the whole object while the method is being run by one thread. synchronized blocks acquires a lock in the object between parentheses after the synchronized keyword.

Which is more efficient synchronized method or synchronized block?

The system performance may degrade because of the slower working of synchronized keyword. Java synchronized block is more efficient than Java synchronized method.


2 Answers

Yes, there is an additional performance cost, unless and until the JVM inlines the call to y(), which a modern JIT compiler will do in fairly short order. First, consider the case you've presented in which y() is visible outside the class. In this case, the JVM must check on entering y() to ensure that it can enter the monitor on the object; this check will always succeed when the call is coming from x(), but it can't be skipped, because the call could be coming from a client outside the class. This additional check incurs a small cost.

Additionally, consider the case in which y() is private. In this case, the compiler still does not optimize away the synchronization; see the following disassembly of an empty y():

private synchronized void y();
  flags: ACC_PRIVATE, ACC_SYNCHRONIZED
  Code:
    stack=0, locals=1, args_size=1
       0: return

According to the spec's definition of synchronized, each entrance into a synchronized block or method performs lock action on the object, and leaving performs an unlock action. No other thread can acquire that object's monitor until the lock counter goes down to zero. Presumably some sort of static analysis could demonstrate that a private synchronized method is only ever called from within other synchronized methods, but Java's multi-source-file support would make that fragile at best, even ignoring reflection. This means that the JVM must still increment the counter on entering y():

Monitor entry on invocation of a synchronized method, and monitor exit on its return, are handled implicitly by the Java Virtual Machine's method invocation and return instructions, as if monitorenter and monitorexit were used.

@AmolSonawane correctly notes that the JVM may optimize this code at runtime by performing lock coarsening, essentially inlining the y() method. In this case, after the JVM has decided to perform a JIT optimization, calls from x() to y() will not incur any additional performance overhead, but of course calls directly to y() from any other location will still need to acquire the monitor separately.

like image 136
chrylis -cautiouslyoptimistic- Avatar answered Oct 15 '22 18:10

chrylis -cautiouslyoptimistic-


Results of a micro benchmark run with jmh

Benchmark                      Mean     Mean error    Units
c.a.p.SO18996783.syncOnce      21.003        0.091  nsec/op
c.a.p.SO18996783.syncTwice     20.937        0.108  nsec/op

=> no statistical difference.

Looking at the generated assembly shows that lock coarsening has been performed and y_sync has been inlined in x_sync although it is synchronized.

Full results:

Benchmarks: 
# Running: com.assylias.performance.SO18996783.syncOnce
Iteration   1 (5000ms in 1 thread): 21.049 nsec/op
Iteration   2 (5000ms in 1 thread): 21.052 nsec/op
Iteration   3 (5000ms in 1 thread): 20.959 nsec/op
Iteration   4 (5000ms in 1 thread): 20.977 nsec/op
Iteration   5 (5000ms in 1 thread): 20.977 nsec/op

Run result "syncOnce": 21.003 ±(95%) 0.055 ±(99%) 0.091 nsec/op
Run statistics "syncOnce": min = 20.959, avg = 21.003, max = 21.052, stdev = 0.044
Run confidence intervals "syncOnce": 95% [20.948, 21.058], 99% [20.912, 21.094]

Benchmarks: 
com.assylias.performance.SO18996783.syncTwice
Iteration   1 (5000ms in 1 thread): 21.006 nsec/op
Iteration   2 (5000ms in 1 thread): 20.954 nsec/op
Iteration   3 (5000ms in 1 thread): 20.953 nsec/op
Iteration   4 (5000ms in 1 thread): 20.869 nsec/op
Iteration   5 (5000ms in 1 thread): 20.903 nsec/op

Run result "syncTwice": 20.937 ±(95%) 0.065 ±(99%) 0.108 nsec/op
Run statistics "syncTwice": min = 20.869, avg = 20.937, max = 21.006, stdev = 0.052
Run confidence intervals "syncTwice": 95% [20.872, 21.002], 99% [20.829, 21.045]
like image 32
assylias Avatar answered Oct 15 '22 17:10

assylias