Michael's Notes & Blog

ReentrantLock in Java

The advantage if ReentrantLock over intrinsic Lock

  • The thread waiting to acquire a lock can be interrupted if lockInterruptibly() is used
  • A timeout can be set by using tryLock()
  • Hand-over-Hand Locking is possible which enables efficient concurrent access on data structures like linked list
  • Conditional variables is introduced pattern to use conditional variables
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
   while (!<<condition is true>>)
       condition.await();
   « use shared resource>>
} finally {lock.unlock(); }

A thread must hold the associated lock before being able to wait on the condition. if the condition is not true, it calls await() , which atomically unlocks the lock and blocks on the condition variable.

When another thread calls singal() or singnalAll() to indicate that the condition might now be true, await() unblocks and automatically reacquires the lock. An important point is that when await() returns, it only indicates that the condition might be true. so we need to go back and recheck whether the condition is true.

From my understanding, the advantage of condition is that we can use signal() and await() to get some coordination between threads, instead of trying to acquire the lock blindly.

How can we solve the Dining Philosoper problem using condition? In order to eat, a philosoper need to have his left chopstick and his right chopstick available, so intuitively, we might want to associate a condition with each of the chopstick, and a philosoper can wait on the condition associated with the chopsitck on his left and the chopsitck on his right in order to eat. But, the issue is, waiting on two conditions is messy. So, instead, we associate a condition with each of the philosoper, and the condition only means the philosoper may go ahead to try to eat, but it's not guaranteed that both of the chopsticks are available. When a philosoper finishes eating, he needs to singal both the philosoper on his left and the philosoper on his right. In order to eat, a philosoper needs to make sure that both pholosopers on his left and his right is not eating.

Fairness

If fairness parameter is set to true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular acces 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. 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.

Spurious wakeup

A spurious wakeup happens when a thread wakes up from waiting on a condition variable that's been signaled, only to discover that the condition it was waiting for isn't satisfied.

Because spurious wakeups can happen whenever there's a race and possibly even in the absence of a race or a signal, when a thread wakes on a condition variable, it should always check that the condition it sought is satisfied.