In ReentrantReadWriteLock documentation it is said:
writer can acquire the read lock, but not vice-versa
If I understand correctly it means that from the same thread you can execute:
//thread1
lock.writeLock().lock()
lock.readLock().lock()
print("this line executes")
This makes sense: if you already locked write no other thread can enter the locked code. But if you locked read, why can't you enter the write block in the same thread if no other thread make read lock? So this doesn't work:
//thread1
lock.readLock().lock()
lock.writeLock().lock()
print("this line doesn't execute")
Why do you have to unlock the read before locking write in the same thread?
ReentrantReadWriteLock doesn't mean that the normal rules locking are not followed.
If a thread acquired a lock for reading purpose, it expects the value of the target data not to change for the duration of the lock. Having otherwise would prevent repeatable-reads. Conceptually, if you let a thread (the same or another) acquire a write lock when a read lock is out, you are breaching that rule.
If a thread acquired a lock for writing, it implicitly has reading rights as well, so acquisition of a read-lock can be granted because it doesn't really breach the contract of the lock (if I can write, I can read) nor the expectations of the lock holder (I locked for writing so I am the only one that can read or write now).
I don't actually know the answer, but it may be to help you avoid writing code that can deadlock.
Suppose you have two threads that execute the same code. Both threads have acquired a read lock. Then, both threads attempt to acquire the write lock. Neither thread will be able to proceed until the other thread releases its read lock, but neither thread will release it's read lock while it's waiting to upgrade.
A different implementation could detect the deadlock, and throw exceptions in both threads when it is discovered, but maybe someone thought that would either (A) adversely impact the performance for applications that do not require deadlock detection, or (B) complicate the API too much.
By disallowing you to upgrade a read lock to a write lock, they've made it impossible for you to write a program that deadlocks in that particular way.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last month.
Improve this question
While a write lock is being held, Read lock can be acquired. But not vice versa. What is the rationale for this design choice.
public static void main(String[] args)
{
System.out.println("read to write test");
ReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
lock.readLock().lock();
System.out.println("Read locks can be acquired after Write locks are acquired as well.");
}
Output of above code:
read to write test
Read locks can be acquired after Write locks are acquired as well.
Vice versa doesn't work.
public static void main(String[] args) throws InterruptedException {
ReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
System.out.println("Read lock is acquired");
System.out.print("Trying to get write lock. ");
boolean writeLockAcquired = lock.writeLock().tryLock(120, TimeUnit.SECONDS);
if (! writeLockAcquired){
System.out.println("Even after 120 seconds, we couldn't get write lock");
}
else{
System.out.println("We could get write lock.");
}
}
Here is the output:
Read lock is acquired
Trying to get write lock. Even after 120 seconds, we couldn't get write lock
Can some one point to snippets in Java open source projects to look at practical usage of Read Write locks. I understand that there are a lot of Java open source projects like ElasticSearch / Hadoop / Spark etc.
While a write lock is being held, Read lock can be acquired.
Locking is to protect against access by concurrent threads.
A read-write lock allows multiple concurrent readers OR a single writer thread.
If a thread owns the write lock, it already has exclusive access, so there's no harm in granting it read access. It will not conflict with itself.
If a thread owns the read lock, there is no guarantee it has exclusive access, so granting the write lock is disallowed. There is no benefit in special-casing the 'single thread wanting both locks' situation.
In other systems, the lock modes are referred to as 'exclusive' (= write) and 'shared' (= read) modes. Considering those terms might help clarify the situation.
There is further rationale in the documentation for the reentrant read-write lock:
Additionally, a writer can acquire the read lock, but not vice-versa.
Among other applications, reentrancy can be useful when write locks
are held during calls or callbacks to methods that perform reads under
read locks. If a reader tries to acquire the write lock it will never
succeed.
Allowing a read lock to be upgraded to a write lock would be a source of deadlocks:
Thread A takes a read lock (now no one can take a write lock)
Thread B takes a read lock
Thread A decides to upgrade to a write lock -- blocks
Thread B decides to upgrade to a write lock -- deadlock
The correct way to do this (when you are doing an operation which may need a write lock, but usually doesn't) is to retry it:
Thread A takes a read lock
Thread B takes a read lock
Thread A decides it needs a write lock
Thread A drops the read lock
Thread A tries to take the write lock -- blocks because B currently holds the read lock
Thread B decides it needs a write lock
Thread B drops the read lock
Thread A gets the write lock
Thread B tries to take the write lock and blocks
Thread A finishes its work and releases the lock
Thread B takes the write lock
I am trying to analyze a thread dump which seems to indicate that there are numerous threads that are waiting on java.util.concurrent.Semaphore permits, i.e., the threads are waiting on Semaphore.acquire().
This I was able to imply because the threads are in WAITING (parking) state, and from what I've understood, Semaphore's do not use LOCK monitors, but use LockSupport.park() instead, waiting on another thread to unpark it.
Now, is there a way to imply from a thread dump on what all threads currently hold the Semaphore permits?
Similar to finding threads in BLOCKED state, and check which is the thread that holds the LOCK which is causing the thread to BLOCK?
Semaphores do not have a concept of ownership or know anything about threads. This makes them particularly lightweight (and useful in asynchronous programming where your logical thread of execution and the hardware thread on which it is executed won't necessarily have a 1:1 mapping).
You can also see this from the fact that a thread can release a semaphore without ever having acquired it.
You will have to look at the stacktraces to see where on what semaphores the threads are waiting and work backwards from there.
There are tools that help you to analyse dumps.Yourkit is one such tool that can be used to analyse blocked threads.
Reference:
https://www.yourkit.com/docs/java/help/monitor_profiling.jsp
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
What I know is:
ReadLock and WriteLock affect each other somehow
WriteLock is just like synchronized
ReadLock seems cannot work alone
readLock.lock();
This means that if any other thread is writing (i.e. holds a
write lock) then stop here until no other thread is writing.
Once the lock is granted no other thread will be allowed to write
(i.e. take a write lock) until the lock is released.
writeLock.lock();
This means that if any other thread is reading or writing, stop
here and wait until no other thread is reading or writing.
Once the lock is granted, no other thread will be allowed to read
or write (i.e. take a read or write lock) until the lock is released.
Combining these you can arrange for only one thread at a time to have write access, but as many readers as you like can read at the same time except when a thread is writing.
Put another way. Every time you want to read from the structure, take a read lock. Every time you want to write, take a write lock. This way whenever a write happens no-one is reading (you can imagine you have exclusive access), but there can be many readers reading at the same time so long as no-one is writing.
The documentation for ReadWriteLock makes this clear:
A ReadWriteLock maintains a pair of associated locks, one for read-only operations and one for writing. The read lock may be held simultaneously by multiple reader threads, so long as there are no writers. The write lock is exclusive.
So you can have many readers at a time, but only one writer - and the writer will prevent readers from reading, too. This is useful if you've got some resource which is safe to read from multiple threads, and where reading is much more common than writing, but when the resource is not actually read-only. (If there are no writers and reading is safe, there's no need for a lock at all.)
When a thread acquires a WriteLock, no other thread can acquire the ReadLock nor the WriteLock of the same instance of ReentrantReadWriteLock, unless that thread releases the lock. However, multiple threads can acquire the ReadLock at the same time.
Using ReadWriteLock, you can improve performance of an application in which more reads are performed on a shared object than writes.
ReadWriteLock maintains two locks for read and write operations. Only one lock either read or write can be acquired at the same time. But multiple threads can simultaneously acquire read lock provided write lock is not acquired by any thread.
ReentrantReadWriteLock is an implementation of ReadWriteLock. It gives write lock to the longest waiting thread if multiple thread are not waiting for read lock. If multiple threads are waiting for read lock, read lock is granted to them.
A reader which acquired read lock can reacquire read lock, similarly, writer can reacquire write lock and can acquire read lock also.
See http://www.zoftino.com/java-concurrency-lock-and-condition-examples
Consider a situation: In a case when data structures are read-mostly - they are mutable and are sometimes modified, but most accesses involve mostly reading, so in these case we can relax the locking mechanism in away that we can allow multiple readers to access the data structures instead of readers waiting while one reader has released the lock. As long as each thread is guaranteed an up to date view of the shared data and no thread modifies it while the readers are viewing it, there will no problems. This is what read write allows : a resource can be accessed by multiple readers or a single writer at a time, but not both.
If the thread holding a ReentrantReadWriteLock.writeLock() stops executing due to an uncaught exception, is the lock released, or is it held and all the other threads are now deadlocked?
I'll assume that by "fail", you mean an uncaught exception propagates off the top of the Thread's run method, causing it to stop executing.
If the thread used finally blocks properly, then it will have unlocked the writeLock on its way back up the stack.
If the thread didn't call unlock(), however, it still holds that monitor even though it's not running any more - so yes, other threads will be deadlocked.
This is why it's critical important to acquire and release resources correctly. And also a reason to stick with synchronized blocks unless/until you can establish that you need the functionality of specific locks - because they cannot fail to be released. (In your case I'm sure you do need the separate read/write locks, I'm making a more general point here.)
You must use a try-finally block when using "Explicit Lock", to release any lock that you acquired.
This is a key difference between using synchronized.