Java Concurrency in Practice (2006) has a section titled "Choosing Between Synchronized and ReentrantLock". As far a performance in concerned we have
"The performance of ReentrantLock appears to dominate that of intrinsic locking, winning slightly on Java 6 and dramatically on Java 5.0" as well as
"Future performance improvements are likely to favor synchronized over ReentrantLock".
Here is a quick throughput benchmark comparing the two on the current Java 8
The setup
Java version:
> java -version
java version "1.8.0_71"
Java(TM) SE Runtime Environment (build 1.8.0_71-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.71-b15, mixed mode)
wmic cpu output (partial):
Name NumberOfCores
Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz 4
In the JMH benchmark attached bellow, a shared counter is incremented by multiple threads. Before incrementing, a thread acquires a lock using either synchronized or a ReentrantLock.
It is executed with N = [1..4]:
> java -jar target\benchmarks.jar concurrent -t N -wi 10 -i 20 -rf csv -tu ms -si true
Here is the complete source (Gist
link):
package concurrent;
/**
* java -jar target\benchmarks.jar concurrent -t N -wi 10 -i 20 -rf csv -tu ms -si true
*/
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@State(Scope.Benchmark)
public class ReentrantLockVsSynchronized {
@Param({"ReentrantLock", "Synchronized"})
private String lockMode;
@State(Scope.Benchmark)
public static class Counter {
private static long value;
}
@State(Scope.Benchmark)
public static class JvmLock {
private static Object lock = new Object();
}
@State(Scope.Benchmark)
public static class RLock {
private static Lock lock = new ReentrantLock();
}
@Benchmark
public void measure(JvmLock jvmLock, RLock rLock, Counter counter) {
switch (lockMode) {
case "Synchronized":
doSynchronized(jvmLock, counter);
case "ReentrantLock":
doReentrantLock(rLock, counter);
}
}
public void doReentrantLock(RLock rLock, Counter counter) {
rLock.lock.lock();
try {
Counter.value++;
} finally {
rLock.lock.unlock();
}
}
public void doSynchronized(JvmLock jvmLock, Counter counter) {
synchronized (jvmLock.lock) {
Counter.value++;
}
}
}
The results
In these tests, the throughput of the ReentrantLock is two to three times higher than that of synchronized. There is also a funny dip when N = 2, this was observed before
here .