Why use a ReentrantLock if one can use synchronized(this)?
A ReentrantLock is unstructured, unlike synchronized
constructs -- i.e. you don't need to use a block structure for locking and can even hold a lock across methods. An example:
private ReentrantLock lock;
public void foo() {
...
lock.lock();
...
}
public void bar() {
...
lock.unlock();
...
}
Such flow is impossible to represent via a single monitor in a synchronized
construct.
Aside from that, ReentrantLock
supports lock polling and interruptible lock waits that support time-out. ReentrantLock
also has support for configurable fairness policy, allowing more flexible thread scheduling.
The constructor for this class accepts an optional fairness parameter. When set
true
, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. Note however, that fairness of locks does not guarantee fairness of thread scheduling. Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock. Also note that the untimedtryLock
method does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting.
ReentrantLock
may also be more scalable, performing much better under higher contention. You can read more about this here.
This claim has been contested, however; see the following comment:
In the reentrant lock test, a new lock is created each time, thus there is no exclusive locking and the resulting data is invalid. Also, the IBM link offers no source code for the underlying benchmark so its impossible to characterize whether the test was even conducted correctly.
When should you use ReentrantLock
s? According to that developerWorks article...
The answer is pretty simple -- use it when you actually need something it provides that
synchronized
doesn't, like timed lock waits, interruptible lock waits, non-block-structured locks, multiple condition variables, or lock polling.ReentrantLock
also has scalability benefits, and you should use it if you actually have a situation that exhibits high contention, but remember that the vast majority ofsynchronized
blocks hardly ever exhibit any contention, let alone high contention. I would advise developing with synchronization until synchronization has proven to be inadequate, rather than simply assuming "the performance will be better" if you useReentrantLock
. Remember, these are advanced tools for advanced users. (And truly advanced users tend to prefer the simplest tools they can find until they're convinced the simple tools are inadequate.) As always, make it right first, and then worry about whether or not you have to make it faster.
One final aspect that's gonna become more relevant in the near future has to do with Java 15 and Project Loom. In the (new) world of virtual threads, the underlying scheduler would be able to work much better with ReentrantLock
than it's able to do with synchronized
, that's true at least in the initial Java 15 release but may be optimized later.
In the current Loom implementation, a virtual thread can be pinned in two situations: when there is a native frame on the stack — when Java code calls into native code (JNI) that then calls back into Java — and when inside a
synchronized
block or method. In those cases, blocking the virtual thread will block the physical thread that carries it. Once the native call completes or the monitor released (thesynchronized
block/method is exited) the thread is unpinned.
If you have a common I/O operation guarded by a
synchronized
, replace the monitor with aReentrantLock
to let your application benefit fully from Loom’s scalability boost even before we fix pinning by monitors (or, better yet, use the higher-performanceStampedLock
if you can).
Synchronized Method vs ReentrantLock
In Java synchronized
sections are reentrant. This means that a single thread can enter the synchronized section as many times as required, but a new thread can only enter when no other threads are present. A thread currently within these sections has acquired a lock and will only return the lock upon leaving all synchronized sections. Apart from declaring synchronized
through the method signature, synchronized
can also be called on objects directly. For example; these two methods will have the same effect:
synchronized public void foo() {
}
public void foo() {
synchronized(this) {
}
}
The ReentrantLock
is very similar to synchronized
in that only one thread can acquire the lock at one time. If a thread reaches a lock.lock()
statement it will wait until the lock is unlocked by another thread. If the thread already has the lock it will continue. This can be useful in more complicated situations where a single synchronized code block isn't enough.
What would happen if ... I made the put method synchronized and static?
If the method is static synchronized
that means you are locking the class itself and not the instance of the class. It is locked independently of an instance synchronized
method.
For your code:
The easiest thing to do here would be to make the Data
object into a thread safe object. If you are not able to edit the code for this class, then one valid strategy would be to wrap the object in a thread safe wrapper.
interface Foo {
void bar();
}
class UnsafeFoo implements Foo {
@Override bar() { ... }
}
class ThreadSafeFoo implements Foo {
Foo foo;
ThreadSafeFoo(Foo foo) { this.foo = foo; }
@Override synchronized bar() { foo.bar(); }
}
Foo unsafe = new UnsafeFoo();
Foo safe = new ThreadSafeFoo(unsafe);
Is using ReentrantLock reliable as synchronized?
Only one thread will acquire the lock: this is the contract of ReentrantLock
.
Therefore your example 2 is perfectly thread safe.
is reentrant lock is complete replacement for synchronisation?
The answer is "Yes".
lock
- unlock
pair used instead of synchronize( ) { ... }
.await
and signal
in Condition
is replacement for wait
and notify
.
Using ReentrantLock instead of synchronized for improving performance
You can do the same without synchronization.
Synchronization is needed only if a thread needs to know the value of a variable to update it. If the thread only change a variable it is sufficient to define it as volatile.
public class Container
private volatile Date date;
private volatile int amount;
public void update(int amount){
int actualAmount;
if (check(date)) {
//do some BULK computation to compute the actualAmount
date = new Date();
this.amount = actualAmount;
}
}
private boolean check(Date date) {
//reading from the date and returning true if the date is expired
}
}
NOTE: A synchronization can be necessary if the amount depends on the actual amount value. A synchronization for date is not necessary
Related Topics
Change Background Color of One Cell in Jtable
Disable a Particular Checkstyle Rule for a Particular Line of Code
Why Are the Rsa-Sha256 Signatures I Generate with Openssl and Java Different
What Is the Cross-Platform Way of Obtaining the Path to the Local Application Data Directory
What Are the Differences Between Arraylist and Vector
How to Remove Duplicate White Spaces in String Using Java
Why Is Class.Newinstance() "Evil"
How to Use Aop with Aspectj for Logging
Spring Boot Rest API - Request Timeout
Singleton with Arguments in Java
Compile-Time Constants and Variables
What Does It Mean to Program to an Interface
Printing Runtime Exec() Outputstream to Console
How to Increase Ide Memory Limit in Intellij Idea on MAC