I'm trying to understand what makes the lock in concurrency so important if one can use synchronized (this)
. In the dummy code below, I can do either:
synchronized(this){...}
)Code:
private final ReentrantLock lock = new ReentrantLock();
private static List<Integer> ints;
public Integer getResult(String name) {
.
.
.
lock.lock();
try {
if (ints.size()==3) {
ints=null;
return -9;
}
for (int x=0; x<ints.size(); x++) {
System.out.println("["+name+"] "+x+"/"+ints.size()+". values >>>>"+ints.get(x));
}
} finally {
lock.unlock();
}
return random;
}
This question is related to
java
multithreading
concurrency
synchronize
reentrantlock
From oracle documentation page about ReentrantLock:
A reentrant mutual exclusion Lock with the same basic behaviour and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.
A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking lock will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock.
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.
ReentrantLock key features as per this article
You can use ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock to further acquire control on granular locking on read and write operations.
Have a look at this article by Benjamen on usage of different type of ReentrantLocks
Lets assume this code is running in a thread:
private static ReentrantLock lock = new ReentrantLock();
void accessResource() {
lock.lock();
if( checkSomeCondition() ) {
accessResource();
}
lock.unlock();
}
Because the thread owns the lock it will allow multiple calls to lock(), so it re-enter the lock. This can be achieved with a reference count so it doesn't has to acquire lock again.
One thing to keep in mind is :
The name 'ReentrantLock' gives out a wrong message about other locking mechanism that they are not re-entrant. This is not true. Lock acquired via 'synchronized' is also re-entrant in Java.
Key difference is that 'synchronized' uses intrinsic lock ( one that every Object has ) while Lock API doesn't.
You can use reentrant locks with a fairness policy or timeout to avoid thread starvation. You can apply a thread fairness policy. it will help avoid a thread waiting forever to get to your resources.
private final ReentrantLock lock = new ReentrantLock(true);
//the param true turns on the fairness policy.
The "fairness policy" picks the next runnable thread to execute. It is based on priority, time since last run, blah blah
also, Synchronize can block indefinitely if it cant escape the block. Reentrantlock can have timeout set.
ReentrantReadWriteLock
is a specialized lock whereas synchronized(this)
is a general purpose lock. They are similar but not quite the same.
You are right in that you could use synchronized(this)
instead of ReentrantReadWriteLock
but the opposite is not always true.
If you'd like to better understand what makes ReentrantReadWriteLock
special look up some information about producer-consumer thread synchronization.
In general you can remember that whole-method synchronization and general purpose synchronization (using the synchronized
keyword) can be used in most applications without thinking too much about the semantics of the synchronization but if you need to squeeze performance out of your code you may need to explore other more fine-grained, or special-purpose synchronization mechanisms.
By the way, using synchronized(this)
- and in general locking using a public class instance - can be problematic because it opens up your code to potential dead-locks because somebody else not knowingly might try to lock against your object somewhere else in the program.
I think the wait/notify/notifyAll methods don't belong on the Object class as it pollutes all objects with methods that are rarely used. They make much more sense on a dedicated Lock class. So from this point of view, perhaps it's better to use a tool that is explicitly designed for the job at hand - ie ReentrantLock.
Synchronized locks does not offer any mechanism of waiting queue in which after the execution of one thread any thread running in parallel can acquire the lock. Due to which the thread which is there in the system and running for a longer period of time never gets chance to access the shared resource thus leading to starvation.
Reentrant locks are very much flexible and has a fairness policy in which if a thread is waiting for a longer time and after the completion of the currently executing thread we can make sure that the longer waiting thread gets the chance of accessing the shared resource hereby decreasing the throughput of the system and making it more time consuming.
Source: Stackoverflow.com