I have been reading Andrews's book and don't fully understand how the ReadWriteLock recordNumbersLock is used.
It seems it is to control concurrent access to the map recordNumbers, but I don't see it associated with the map at all. I thought it may get somehow linked or tied to the map it is protecting access to.
So when for example in getDvdList method when a writeLock() is implemented there is a lot of code following it until the lock is unlocked. Does it mean no other thread can access this block of code until the lock is unlocked? So any code within that block is protected not just the recordNumbers map that it is intending to protect?
J Flew wrote:
Does it mean no other thread can access this block of code until the lock is unlocked?
So any code within that block is protected not just the recordNumbers map that it is intending to protect?
What I didn't understand from Sun Java API documentation is when the readLock is used?
If a readLock can be accessed by multiple threads why this readLock is used at all in Andrew's book in the method getDvd(String upc)?
Maybe some other people can clarify this.
As mentioned explicitly in the book, there are times when we deliberately went above and beyond what was absolutely required for the project - mainly so that our sample assignment could not be used as a real assignment. There is a little bit of that in the read lock usage.
As you noted, multiple threads can own a read lock simultaneously. However only one thread can own a write lock at any given time, and no thread can own a write lock while any thread owns a read lock.
How this affects you depends on what your atomic unit of work is. As a minimalistic block, you could have (pseudo-code):
This will certainly work, and the atomic operation (the reading from file) is so small that the difference between using read/write locks versus using one single mutex for file access is not worth worrying about.
However there is a good argument for making the atomic operation larger, making our potential code:
This can make the code easier to read as now the locking and unlocking are the first and last items in the method. This would match up with the logic for writing a record, where you would probably want the entire logic encapsulated in a logical lock.
Since this later version of the code has a lock for a longer period of time, it makes sense to use the read-locks, so that multiple threads can read the same record simultaneously - they can all be doing the initialization logic and/or the conversion logic without worrying that another thread may be writing to that record - as mentioned earlier, as long as there is one read lock on a record, there cannot be a write lock on that record.
Thanks Adrew for your explanation. Finally I understood how read/write locks work. Is no sense to use them individually. They always must be used toghether because they exclude each other. Previously I was testing only a readLock (as I was used to work with one single mutex, using synchronized blocks) and I was not understanding why readLock is used while many threads can own that lock. But the main ideea is that in case of ReentrantReadWriteLock always make sense to work with both read and write locks. As you mentioned, if I understood right, we assure that:
1. While many threads own a read lock -> no thread own a write lock;
(threads that own the read lock execute code block between readLock().lock() and readLock().unlock() interchangeably until they finish executing that block while no thread can execute code block between writeLock().lock() and writeLock().unlock());
2. While only one thread own a write lock -> no other threads own a read lock.
(while only one thread execute code block between writeLock().lock() and writeLock().unlock() until it finish that block code no other threads can execute code block between readLock().lock() and readLock().unlock()).