Search code examples
javamultithreadingreentrantlock

ReeantrantLock and Condition variable


Hello:) I'm learning about the Re-entrant Lock and the condition variable in Java. I happened to come across this tutorial. In the tutorial, the author provides a Producer-Consumer example utilizing the ReentrantLock:

public class CondDemo
{
   public static void main(String[] args)
   {
      Shared s = new Shared();
      new Producer(s).start();
      new Consumer(s).start();
   }
}

class Shared
{
   private volatile char c;
   private volatile boolean available;
   private final Lock lock;
   private final Condition condition;

   Shared()
   {
      c = '\u0000';
      available = false;
      lock = new ReentrantLock();
      condition = lock.newCondition();
   }

   Lock getLock()
   {
      return lock;
   }

   char getSharedChar()
   {
      lock.lock();
      try
      {
         while (!available) {
            try
            {
               condition.await();
            }
            catch (InterruptedException ie)
            {
               ie.printStackTrace();
            }
         }
         available = false;
         condition.signal();
      }
      finally
      {
         lock.unlock();
         return c;
      }
   }

   void setSharedChar(char c)
   {
      lock.lock();
      try
      {
         while (available) {
            try
            {
               condition.await();
            }
            catch (InterruptedException ie)
            {
               ie.printStackTrace();
            }
         }
         this.c = c;
         available = true;
         condition.signal();
      }
      finally
      {
         lock.unlock();
      }
   }
}

class Producer extends Thread
{
   private final Lock l;

   private final Shared s;

   Producer(Shared s)
   {
      this.s = s;
      l = s.getLock();
   }

   @Override
   public void run()
   {
      for (char ch = 'A'; ch <= 'Z'; ch++)
      {
         l.lock();
         s.setSharedChar(ch);
         System.out.println(ch + " produced by producer.");
         l.unlock();
      }
   }
}

class Consumer extends Thread
{
   private final Lock l;

   private final Shared s;

   Consumer(Shared s)
   {
      this.s = s;
      l = s.getLock();
   }

   @Override
   public void run()
   {
      char ch;
      do
      {
         l.lock();
         ch = s.getSharedChar();
         System.out.println(ch + " consumed by consumer.");
         l.unlock();
      }
      while (ch != 'Z');
   }
}

Are the methods l.lock() and l.unlock() in the Producer and Consumer unnecessary? I notice we have already applied the lock/unlock in the Shared object's getSharedChar() and setSharedChar() methods. Or is this a suggested pattern to use the condition variables?

Is this is a not good practice, can you provide a link for a better example?

Thanks:)


Solution

  • That's neither necessary, neither a pattern.

    IMHO This is ugly redundant lock used to have clean output. I mean ugly because there's no try-finally to ensure first level unlock and it exposes internal state of Shared API.

    To go deeper in learning Java concurrency, you can obtain same result using SynchronousQueue or Exchanger