Skip to content

Instantly share code, notes, and snippets.

@vikasverma787
Last active March 17, 2018 08:50
Show Gist options
  • Save vikasverma787/9acfb081c4f4364b8100557635cc6178 to your computer and use it in GitHub Desktop.
Save vikasverma787/9acfb081c4f4364b8100557635cc6178 to your computer and use it in GitHub Desktop.
Java Lock Example Reentrant Lock
Lock : This is the base interface for Lock API. It provides all the features of synchronized keyword with additional ways to create
different Conditions for locking, providing timeout for thread to wait for lock. methods are lock(),unlock(),tryLock(),newCondition()
Condition: Condition objects are similar to Object wait-notify model with additional feature to create different sets of wait.
A Condition object is always created by Lock object. Some of the important methods are await() that is similar to wait() and signal(),
signalAll() that is similar to notify() and notifyAll() methods.
Benefits :
synchronization blocks or methods can cover only one method whereas we can acquire the lock in one method and release it in another method
with Lock API.
synchronized keyword doesn’t provide fairness whereas we can set fairness to true while creating ReentrantLock object so that longest waiting
thread gets the lock first.
One more worth noting difference between ReentrantLock and synchronized keyword in Java is, ability to interrupt Thread while waiting for
Lock. In case of synchronized keyword, a thread can be blocked waiting for lock, for an indefinite period of time and there was no way to
control that. ReentrantLock provides a method called lockInterruptibly(), which can be used to interrupt thread when it is waiting for lock.
Similarly tryLock() with timeout can be used to timeout if lock is not available in certain time period.
4) ReentrantLock also provides convenient method to get List of all threads waiting for lock.
tryLock/timeout for waiting for Lock/lock interrutibility/lock fairness/ get the no of threads watining for lock.
public class ReentrantLockHowto {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
// Locking using Lock and ReentrantLock
public int getCount() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " gets Count: " + count);
return count++;
} finally {
lock.unlock();
}
}
// Implicit locking using synchronized keyword
public synchronized int getCountTwo() {
return count++;
}
public static void main(String args[]) {
final ReentrantLockHowto counter = new ReentrantLockHowto();
Thread t1 = new Thread() {
@Override
public void run() {
while (counter.getCount() < 6) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
while (counter.getCount() < 6) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
};
t1.start();
t2.start();
}
}
Locks In Synchronized Methods :
When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it
when the method returns. The lock release occurs even if the return was caused by an uncaught exception.
Let’s see how the tryLock() works:
public void performTryLock(){
//...
boolean isLockAcquired = lock.tryLock(1, TimeUnit.SECONDS);
if(isLockAcquired) {
try {
//Critical section here
} finally {
lock.unlock();
}
}
//...
}
In this case, the thread calling tryLock(), will wait for one second and will give up waiting if the lock is not available.
ReentrantReadWriteLock
ReentrantReadWriteLock class implements the ReadWriteLock interface.
Let’s see rules for acquiring the ReadLock or WriteLock by a thread:
Read Lock – if no thread acquired the write lock or requested for it then multiple threads can acquire the read lock
Write Lock – if no threads are reading or writing then only one thread can acquire the write lock
Let’s see how to make use of the ReadWriteLock:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class SynchronizedHashMapWithReadWriteLock {
Map<String,String> syncHashMap = new HashMap<>();
ReadWriteLock lock = new ReentrantReadWriteLock();
//...
Lock writeLock = lock.writeLock();
public void put(String key, String value) {
try {
writeLock.lock();
syncHashMap.put(key, value);
} finally {
writeLock.unlock();
}
}
...
public String remove(String key){
try {
writeLock.lock();
return syncHashMap.remove(key);
} finally {
writeLock.unlock();
}
}
//...
}
For both the write methods, we need to surround the critical section with the write lock, only one thread can get access to it:
Lock readLock = lock.readLock();
//...
public String get(String key){
try {
readLock.lock();
return syncHashMap.get(key);
} finally {
readLock.unlock();
}
}
public boolean containsKey(String key) {
try {
readLock.lock();
return syncHashMap.containsKey(key);
} finally {
readLock.unlock();
}
}
For both read methods, we need to surround the critical section with the read lock. Multiple threads can get access to this section if no
write operation is in progress.
Working with Conditions:
public class ReentrantLockWithCondition {
Stack<String> stack = new Stack<>();
int CAPACITY = 5;
ReentrantLock lock = new ReentrantLock();
Condition stackEmptyCondition = lock.newCondition();
Condition stackFullCondition = lock.newCondition();
public void pushToStack(String item){
try {
lock.lock();
while(stack.size() == CAPACITY){
stackFullCondition.await();
}
stack.push(item);
stackEmptyCondition.signalAll();
} finally {
lock.unlock();
}
}
public String popFromStack() {
try {
lock.lock();
while(stack.size() == 0){
stackEmptyCondition.await();
}
return stack.pop();
} finally {
stackFullCondition.signalAll();
lock.unlock();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment