Is there any difference in performance between this
synchronized void x() {
y();
}
synchronized void y() {
}
and this
synchronized void x() {
y();
}
void y() {
}
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.
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.
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.
The system performance may degrade because of the slower working of synchronized keyword. Java synchronized block is more efficient than Java synchronized method.
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.
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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With