Why intrinsic lock like synchronized is discouraged [duplicate] - java

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 the entire method or synchronize the vulnerable area (synchronized(this){...})
OR lock the vulnerable code area with a ReentrantLock.
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;
}

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).

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.

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
Ability to lock interruptibly.
Ability to timeout while waiting for lock.
Power to create fair lock.
API to get list of waiting thread for lock.
Flexibility to try for lock without blocking.
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

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.

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.

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.

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.

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.

Related

Is it a bad practice to use pure lock() method in Java?

According to documentation:
void lock()
Acquires the lock.
If the lock is not available then the current thread becomes disabled for thread scheduling purposes and lies dormant until the lock has been acquired.
I'm using timed tryLock nearly everywhere, handling all what is expected - if timout ends, if it is interrupted, and so on.
But today, I've thought: is it bad/unsafe/dangerous to use pure lock()?
Examples showing it looking so cute and elegant - just lock and unlock in finally. I've googled, but haven't found answer about it being a bad practice.
It depends on how you use it inside of your code. If you just use it, only for lock and unlock, then you don't use any of the features it provides. So, you can merely fallback to intrinsic locks using the keyword synchronized. However, if you use timed lock and interruptible locking facilities, then it may be reasonable given your specific usecase warrants it. Intrinsic locks still have significant advantages over explicit locks. The notation is familiar and compact. Oftentimes, developers forget to call unlock on explicit locks, making it much more prone to errors.
Moreover, if you use condition objects you can still use either of them. If you have only one condition for the lock object, I would rather use intrinsic locks. The explicit locks become handy when you need to have multiple conditions for a given lock and need to use it without any interference from other conditions on the same lock.
In conclusion, it all depends on how you use it and whether your usecase warrants it.
There's nothing to say if its bad or good. If you need to handle critical blocks, you need to lock it.

locking synchronization in java using reentrant lock

I have and application made in java that uses an reentrant global lock and I have a problem like this:
One thread is acquire the reentrant global lock and let say that is keeping the lock 30 seconds in this interval of time it makes some operations. In this interval of time all other threads are blocked.
My problem is that I want some threads like RMI to have the chance to execute.
What would be a good locking policy or optimization in order to let some other threads to acquire the lock for a short period of time ?
So you basically have a job queue which should be executed in a single-threaded environent. Each time before polling from this queue you need to sort it's entries by priority.
abstract class JobEntry<V> implements Callable<V>{
Date registeredAt;
long runEstimationMs;
JobType type;
}
So you could come up with a weighing function for this entry and sort entries based on it or with implementing Comparable.
And this is almost it. You could send these jobs to a fixed thread pool of a single thread. If you need to interrupt them, you would need to cancel the future and each job should check Thread.interrupted() state.
The most difficult part here is the weighing function, a possible way to build it could be making a set of experiments on your system.
If one thread has acquired lock other threads cant proceed , we can not change this behavior.
Now to solve your problem there are few suggestions I want to give.
Try to Reduce lock scope so that other threads also get chance for execution.
Look at the possibility if you can acquire lock for really required part and release lock after that. Because as you said one thread is taking good amount of time there could be some part of code where you don't need locking.
operationInLock(){
----
lock.lock();
Code where lock is required;
lock.unlock();
Code where lock is Not required;
Code where lock is Not required;
Code where lock is Not required;
lock.lock();
Code where lock is required;
lock.unlock();
Code where lock is Not required;
Code where lock is Not required;
Code where lock is Not required;
}
If you don't feel this answer useful , give us some more info about code/functionality because without seeing code it becomes very difficult to give solution we can just give u suggestions based on best practice.
I believe this can be achieved via acquiring on only particular section of code by first thread which has global lock for 30 seconds and also you can also use Read and write separate locks which comes with ReadWriteLock Object in java.
ReadWriteLock is implemented by ReentrantReadWriteLock Class in java.util.concurrent.locks package.Multiple Threads can acquire multiple read Locks, but only a single Thread can acquire mutually-exclusive write Lock .Other threads requesting readLocks have to wait till the write Lock is released. A thread is allowed to degrade from write lock to read lock but not vice-versa. Allowing a read thread to upgrade would lead to a deadlock as more than one thread can try to upgrade its lock. The ReentrantReadWriteLock also supports all the features of the Reentrant lock like providing the fair mechanism, reentrant locks, Condition Support (on a write Lock only), allowing interruption on read as well as write Locks.
. A Condition object, also known as condition variable, provides a thread with the ability to suspend its execution, until the condition is true. A Condition object is necessarily bound to a Lock and can be obtained using the newCondition() method.
Furthermore, a Condition enables the effect of having multiple wait-sets per object, by combining these sets with the use of a Lock implementation. Moreover, due to the fact that Conditions access portions of state shared among different threads, the usage of a Lock is mandatory. It is important to mention that a Condition must atomically release the associated Lock and suspend the current’s thread execution.
For your reference I am giving you the URLs ->
https://examples.javacodegeeks.com/core-java/util/concurrent/locks-concurrent/condition/java-util-concurrent-locks-condition-example/
https://examples.javacodegeeks.com/core-java/util/concurrent/locks-concurrent/readwritelock/java-readwritelock-example/
Please let me know if you need other help

how using Lock interface gives more performance over using synchronise keyword in concurrent applications design?

I was going through "Java Concurrency CookBook". In that author mentioned using Lock interface gives more performance over using synchronized keyword.Can any one tell how? Using the terms like stack-frame, ornumber of method calls.
Don't mind, please help me get rid of java concurrency concepts.
The raison d'etre for Lock and friends isn't that it is inherently faster than synchronized(), it is that it can be used in different ways that don't necessarily correspond to the lexical block structure, and also that it can offer more facilities such as read-write locks, counting semaphores, etc.
Whether a specific Lock implementation is actually faster than synchronized is a moot point and implementation-dependent. There is certainly no such claim in the Javadoc. Doug Leas's book[1] where it all started doesn't make any claim that I can see quickly stronger than 'often with better performance'.
[1]: Lea, Concurrent Programming in Java, 2nd edition, Addison Wesley 2000.
1 Synchronisation is the only culprit that leads to the problem of deadlock unlike lock which is free of deadlock issue.
2 In synchronisation , we don’t know after how much time a thread will get a chance after a previous thread has released the lock. This can lead to problem of starvation whereas incase of lock we have its implementing class reentrant lock which has one of its constructor which lets you pass fairness property as one of its argument that leta longest waiting thread get the chance to acquire the lock.
3 In synchronisation, if a thread is waiting for another thread, then the waiting thread won’t do any other activity which doesn’t require lock access but with lock interface there is a trylock() method with which you can try for access the lock and if you don’t get the lock you can perform other alternate tasks. This helps to improve the performance of the application .
4 There is no api to check how many threads are waiting for a particular lock whereas this is possible with lock interface implementation class ReentrantLock methods.
5 One can get better control of locks using lock interface with holdCount() method which is not found with synchronization.

Explicit Locks vs Implicit Locks

Is using Locks (java.util.concurrent.locks.Lock) instead of keyword synchronized + method wait() and method notify() totally the same?
Can I thread-safely program using locks (explicit locks) rather than implicit locks (synchronized)?
As of know I have always been using implicit locks. I am aware of the advantages given by the Lock interface implementation like methods: isLocked(), getLockQueueLength(), getHoldCount(), etc... however still the old school way (wait() and notify()) would have other limits other than not having those methods?
I am also aware of the possibility of constructing a lock with a (boolean fairness) parameter which allows lack of starvation.
Yes, absolutely you can write thread-safe program using java.util.concurrent.locks.Lock. If you see any implementation of java.util.concurrent.locks.Lock like ReentrantLock internal implementation uses old synchronized blocks.
Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects.
Adding to my difference the synchronized keyword has naturally built in language support. This can mean the JIT can optimise synchronised blocks in ways it cannot with Locks. e.g. it can combine synchronized blocks.synchronized is best for a small number of threads accessing a lock and Lock may be best for a high number of threads accessing the same locks . Also synchronized block makes no guarantees about the sequence in which threads waiting to entering it are granted access.
Locks and synchronized blocks have the same semantics and provide the same guarantees from a Java Memory Model perspective. The main difference is that Locks provide more control (such as with tryLock or when asking a lock to be fair etc.) which allow for a more flexible and fine-grained lock management.
However, when you don't need those additional features, it is better to use a plain old synchronized block as it reduces the room for error (e.g. you can't "forget" to unlock it).

Why use a ReentrantLock if one can use synchronized(this)?

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 the entire method or synchronize the vulnerable area (synchronized(this){...})
OR lock the vulnerable code area with a ReentrantLock.
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;
}
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).
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.
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
Ability to lock interruptibly.
Ability to timeout while waiting for lock.
Power to create fair lock.
API to get list of waiting thread for lock.
Flexibility to try for lock without blocking.
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
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.
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.
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.
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.
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.

Categories