Why Use a Reentrantlock If One Can Use Synchronized(This)

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 untimed tryLock 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 ReentrantLocks? 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 of synchronized 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 use ReentrantLock. 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 (the synchronized block/method is exited) the thread is unpinned.

If you have a common I/O operation guarded by a synchronized, replace the monitor with a ReentrantLock to let your application benefit fully from Loom’s scalability boost even before we fix pinning by monitors (or, better yet, use the higher-performance StampedLock 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



Leave a reply



Submit