Evolution of Locks and Locking

Harry-Houdini-007 From time immemorial, Java (both the language and the platform) have supported threads. With threading, you’ll need a way to prevent 2 or more threads from stomping over each other. So the earliest version of Java included the synchronized keyword. The following is a contrived example of a shared shopping list; thread safety is maintained using the synchronized

public class ShoppingList { 
   private final List list = new LinkedList<>();
   public synchronized void add(final String item) { 
      list.add(item); 
   }
   public synchronized int count() { 
      return (list.size()); 
   }
}

The synchronized keywords will try to acquire the lock/monitor of the object before entering the methods. If you want to confine the critical region to a smaller area, as oppose to the entire method, then use

synchronized(this) { … }

instead.

JavaSE 5 introduces Lock and CAS APIs into the platform. With the new Lock API, instead of using synchronized you can do the following

public class ShoppingList {
   private final Lock lock = new ReentrantLock();
   private final List list = new LinkedList<>();
   public void add(final String item) {
      lock.lock();
      try {
         list.add(item);
      } finally {

         lock.unlock();
      }
   }
   public int count() {
      lock.lock();
      try {
         return (list.size());
    
} finally { 
         lock.unlock();  
      }      

      return (-1);
   }
}

The advantage of Lock over synchronized is that

  • You can use different lock semantics. The above example I’ve used an exclusive lock  I can also use ReadWriteLock.
  • Flexibility because now you can pass the lock from one thread to another thread or hold a lock over multiple invocation (may not be a good idea)
  • Specify how long to wait for lock by specifying a timeout in tryLock() when acquiring a lock
  • Create multiple conditions for a lock
  • Etc.

One of the MAJOR feature that is missing from Lock is that it does not automatically release the lock. So as a safety precaution, Lock are almost always used inside a try/finally block to ensure that locks are released before we exit the method.

JavaSE 7 introduces the try-with-resource syntax. The idea of the try-with-resource is that resources such as IO streams, ResultSet , etc are automatically closed when they exit the try block. All these resources have to do is to implement the AutoClosable interface.

We can use try-with-resource to automatically release the lock as we exit the try block. There are many ways to implement this. I’m going to go with the simplest

public class AutoUnlock implements AutoClosable {
   private final Lock lock;
   public AutoUnlock(Lock l) {
      lock = l;
   }
   @Override
   public void close() throws Exception {
      lock.unlock(); 

   }
}

Now to use if we can rewrite our ShoppingList as follows

public class ShoppingList {
   private final Lock lock = new ReentrantLock();
   private final List list = new LinkedList<>();
   public void add(final String item) {
      lock.lock();
      try (AutoUnlock ul = new AutoUnlock(lock)) {
         list.add(item);
      } catch (Exception ex) { }
   }
   public int count() {
      lock.lock();
      try (AutoUnlock ul = new AutoUnlock(lock)) {
         return (list.size());
      } catch (Exception ex) { }
      return (-1);
   }
}

Conceptually this is code is very similar to the version with try/finally. The only difference here is that here the unlock is hidden in close().

JavaSE 8 introduces Lambda. Using Lambda, we can now remove the lock()/unlock() combo from all the methods and put them into guards (consider acquiring a lock as a pre condition). Here is a rewrite of ShoppingList

public class ShoppingList {
   private final Lock lock = new ReentrantLock();
   private final List<String> list = new LinkedList<>();
   public void add(final String item) {
      list.add(item);
   }
   public int count() {
      return (list.size());
   } 
   public void guard(final Runnable r) {
      lock.lock();
      try (AutoUnlock ul = new AutoUnlock(lock)) {
         r.run();
      } catch (Exception ex) { }
   }
   public <T> Optional<T> guard(final Callable<T> c) {
      lock.lock();
      try (AutoUnlock ul = new AutoUnlock(lock)) {
         return (Optional.of(c.call()));
      } catch (Exception ex) { }
      return (Optional.empty());
   }
}

With this approach, you can now decide if you prefer the threadsafe or the regular method.

shoppingList.guard(() –> { shoppingList.addItem(“harry”); });

Till next time

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: