1

ReentrantLock allow threads to enter into lock on a resource more than once,

How does this benefit in terms of execution/efficiency/functionality ?

Refer this link, https://www.geeksforgeeks.org/reentrant-lock-java/

i did not get the meaning of using inner lock, because once outer lock is acquired by any of the thread, no other thread is going to enter into the section after outer lock(till the time lock is holded by this thread), and its sure that the section following/after outer lock will only be executed by one thread at a time, then whats the point of inner lock there, implying whats th point of entering into lock more than once ?

CODE:

import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.locks.ReentrantLock; 

class worker implements Runnable 
{ 
  String name; 
  ReentrantLock re; 
  public worker(ReentrantLock rl, String n) 
  { 
    re = rl; 
    name = n; 
  } 
  public void run() 
  { 
    boolean done = false; 
    while (!done) 
    { 
      //Getting Outer Lock 
      boolean ans = re.tryLock(); 

      // Returns True if lock is free 
      if(ans) 
      { 
        try
        { 
          Date d = new Date(); 
          SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); 
          System.out.println("task name - "+ name 
                     + " outer lock acquired at "
                     + ft.format(d) 
                     + " Doing outer work"); 
          Thread.sleep(1500); 

          // Getting Inner Lock 
          re.lock(); 
          try
          { 
            d = new Date(); 
            ft = new SimpleDateFormat("hh:mm:ss"); 
            System.out.println("task name - "+ name 
                       + " inner lock acquired at "
                       + ft.format(d) 
                       + " Doing inner work"); 
            System.out.println("Lock Hold Count - "+ re.getHoldCount()); 
            Thread.sleep(1500); 
          } 
          catch(InterruptedException e) 
          { 
            e.printStackTrace(); 
          } 
          finally
          { 
            //Inner lock release 
            System.out.println("task name - " + name + 
                       " releasing inner lock"); 

            re.unlock(); 
          } 
          System.out.println("Lock Hold Count - " + re.getHoldCount()); 
          System.out.println("task name - " + name + " work done"); 

          done = true; 
        } 
        catch(InterruptedException e) 
        { 
          e.printStackTrace(); 
        } 
        finally
        { 
          //Outer lock release 
          System.out.println("task name - " + name + 
                     " releasing outer lock"); 

          re.unlock(); 
          System.out.println("Lock Hold Count - " + 
                       re.getHoldCount()); 
        } 
      } 
      else
      { 
        System.out.println("task name - " + name + 
                      " waiting for lock"); 
        try
        { 
          Thread.sleep(1000); 
        } 
        catch(InterruptedException e) 
        { 
          e.printStackTrace(); 
        } 
      } 
    } 
  } 
} 

public class test 
{ 
  static final int MAX_T = 2; 
  public static void main(String[] args) 
  { 
    ReentrantLock rel = new ReentrantLock(); 
    ExecutorService pool = Executors.newFixedThreadPool(MAX_T); 
    Runnable w1 = new worker(rel, "Job1"); 
    Runnable w2 = new worker(rel, "Job2"); 
    Runnable w3 = new worker(rel, "Job3"); 
    Runnable w4 = new worker(rel, "Job4"); 
    pool.execute(w1); 
    pool.execute(w2); 
    pool.execute(w3); 
    pool.execute(w4); 
    pool.shutdown(); 
  } 
}
Ankur Goel
  • 311
  • 1
  • 11

2 Answers2

0

I think it's redundant to explain how it works, we should use only one of the methods lock() or tryLock() :

If your thread has multiple tasks to perform, some of which are independent of the lock, then you should use tryLock(). If all tasks your thread needs to perform are dependent on the lock, then you should use lock().

That is, you should use tryLock() over lock() when the thread has or can acquire additional work that is independent of lock acquisition.

----- Optional :

Let's say you have four tasks, 1 through 3 that are executed by a thread pool with two worker threads, A and B. The tasks 1 and 2 share a resource that must be accessed by a single thread at a time to prevent corruption.

Now if you just lock without trial, you could get into the following situation:

  1. Thread A starts task 1;

  2. Thread A acquires the resource lock;

  3. Thread B starts task 2;
  4. Thread B attempts to acquire the lock, but is blocked (sleeps);
  5. Thread A finishes task 1, releasing the lock;
  6. Thread B wakes up, but thread A has not switched yet;
  7. Thread A starts task 3;
  8. Thread B attempts to acquire the lock again;
  9. Thread B acquires the lock and finishes task 2;
  10. Thread A finishes task 3.

Note that lock() suspends a thread until the lock is released, so thread B is completely useless until thread A releases the lock. Thread B could have started task 3 instead of waiting for the lock, and finish it in the meantime.

An algorithm using try-lock could execute like this:

  1. Thread A starts task 1;
  2. Thread A tests and acquires the lock;
  3. Thread B starts task 2;
  4. Thread B tests the lock, skipping task 2;
  5. Thread B starts task 3;
  6. Thread A finishes task 1, releasing the lock;
  7. Thread A starts task 2;
  8. Thread B finishes task 3;
  9. There are no more tasks, so thread B sleeps;
  10. Thread A finishes task 2, releasing the lock.

Note that tryLock() does not suspend the calling thread, so the blocking task can be skipped, and thread B executes the non-blocking task instead. If tasks 1 and 2 were long, and there were several other short non-blocking tasks, they could all be finished before task 1 finishes or task 2 starts.

Of course, implementing thread pools and task management is a bit more complicated than just plain locking: tasks might have to be suspended and returned to the pool; and sleeping idle threads should be woken up when any locks are released.

It is worth the trouble if you have many non-blocking tasks (or at least not blocking on the same single lock) along with some blocking tasks, but if all tasks block on the same resource, it would not even be worth implementing multithreading in the first place.

Abdel
  • 582
  • 4
  • 6
  • my concern is not when should we use tryLock or lock, concern is if the lock is acquired once either by tryLock or lock, whats the point of requesting for lock again. Concern is, in which practical scenarios thread need to acquire lock more than once as this is the functionality provided by reentrantlock – Ankur Goel Jan 08 '19 at 09:01
  • i updated the question with a new comment... hope it provides some help :) – Abdel Jan 08 '19 at 09:10
  • Thats exactly what my question is, then whats the benefit of this functionality provided by reentrant lock – Ankur Goel Jan 08 '19 at 09:14
  • https://stackoverflow.com/questions/18596080/java-concurrent-reentrantlock-why-we-want-to-acquire-the-same-lock-multiple-ti – Abdel Jan 08 '19 at 09:20
  • Re, "If your thread has multiple tasks to perform..." Then maybe you should think about using multiple threads. Threads make more sense when each one waits for just one thing. I've never seen any real code that calls `lock.tryLock()`. If I ever _do_ see such code, I'll start looking for a way to refactor it. – Solomon Slow Jan 08 '19 at 14:25
0

Suppose in a class you have two methods m1 and m2, both synchronized and m2 is calling m1. In that case if thread a has taken a lock on m1 and taking a lock again is not allowed then that thresad will keep on waiting for calling m2 (as it already has a the lock)

more details :

https://stackoverflow.com/questions/18596080/java-concurrent-reentrantlock-why-we-want-to-acquire-the-same-lock-multiple-ti

Abdel
  • 582
  • 4
  • 6