Locks allow you to achieve similar functionality to synchronized methods and synchronized blocks but with additional functionality
git clone https://github.com/spotadev/java-examples.git
Locks have enhanced functionality over and above synchronized methods and blocks. Let us compare them:
A synchronized method or block has the lock within a single method. On the other hand Locks have methods like lock() and unlock() which can be called from different methods.
Synchronized methods or blocks have no concept of fairness. Once a thread has finished executing in the synchronized method or block, the monitor is released and another blocking thread can now become unblocked and take the monitor. If more than one thread is blocked and waiting, there is no concept of who gets to take the monitor next. Ideally the thread which has been waiting the longest should be give the monitor. However synchronized methods and blocks do not have this concept of fairness. Locks on the other hand do have fairness which can be configured.
Threads have the concept of being interrupted when they are blocked waiting. Calling thread.interrupt() will cause the code which is blocking to throw an InterruptedException. This mechanism does not work with threads that are waiting to access a synchronized method or block. It does work with Locks. There is the method "lockInterruptibly()" which can be used instead of lock() which allows the thread to be interrupted.
With synchronized methods and blocks there is no functionality to take the monitor if it is available and not block if it is in usage. With locks there is a method for this: tryLock().
Has the following methods:
void lock() - thread blocks until lock becomes free and cannot be interrupted
void lockInterruptibly() - thread blocks until lock becomes free but can be interrupted
boolean tryLock() - thread takes the monitor if it is available. If it is not available it does not block.
boolean tryLock( Long timeout, TimeUnit timeUnit ) - thread takes the monitor if it is available. If it is not available it waits for the duration specified by timeout until either it gets it or timesout.
void unlock() - the lock is released.
Lock readLock()
Lock writeLock()
2 different locks are given.
The idea is that read locks prevent the write lock getting the monitor. However a read lock thread does not block another read lock thread from reading.
However when a write lock takes the monitor, it blocks other write and read locks.
A thread that aquired the write lock can downgrade to a read lock.
Because the read lock is not exclusive it cannot have a condition associated with it. See below for Conditions. In fact the ReadLock.newCondition() will throw and UnsupportedOperationException.
Implementations:
ReentrantLock
ReentrantReadWriteLock
ReentrayLock with Conditions
StampedLock
Reentrant Lock is an implementation of the Lock interface. It allows the same thread to aquire the lock as many times as it wants - hence the name "Reentrant".
See code example here:
ReentrantReadWriteLock is an implementation of the Reentrant Lock interface
See code example here:
StampedLocks use optimistic locking for read operations. It can improve performance where you have many more readers than writers.
StampedLocks are not reentrant with writeLock.
The idea with optimistic reads is that you first get a stamp from the lock:
long stamp = lock.tryOptimisticRead();
The stamp is a long. Then you retrieve a value from the resource that is shared.
String value = map.get(key);
Then you check if the stamp is still valid:
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
return map.get(key);
} finally {
lock.unlock(stamp);
}
}
If the stamp is not valid that means the value you just retrieved is stale. i.e. a writer has updating or has updated the value since you got the stamp.
If the value is stale, you can drop down to readLock:
stamp = lock.readLock();
So the idea with StampedLock is that it is more efficient for reading if you do not have a lot of writes.
See code example here:
StampedLock - @todo
Back: Concurrency
Page Author: JD