Clean Synchronization Using ReentrantLock and Lambdas

Recently I was reading an informative post about the differences between synchronized vs ReentrantLock by Javin Paul1. He emphasises on the advantages of the latter, but does not withhold some downsides, which are related to the cumbersome try-finally block needed for proper usage.

While agreeing on his statements I brooded about a thought, that always bothers me when it comes down to synchronization. Both approaches mix up separate concerns – synchronization and the functionality of the synchronized content – which hampers testing those concerns one by one.

Being the explorative type, I picked up a solution for this problem that I already tried in the past. However at that time I did not like the programming pattern too much. This was because of its verboseness due to an anonymous class. But having Java 8 and Lambda expressions at hand I thought it might be worth reconsidering. So I copied the ‘counter’ part of Javin Paul’s example, wrote a simple test case and started refactoring. This was the initial situation:

class Counter {

  private final Lock lock;

  private int count;

  Counter() {
    lock = new ReentrantLock();
  }

  int next() {
    lock.lock();
    try {
      return count++;
    } finally {
      lock.unlock();
    }
  }
}

One can clearly see the ugly try-finally block that produces a lot of noise around the actual functionality2. The idea is to move this block into its own class that serves as a synchronization aspect to a kind of operation that does the incremental. The next snippet shows how such a newly created Operation interface may look like and how it can be used by a Lambda expression3:

class Counter {

  private final Lock lock;

  private int count;

  interface Operation<T> {
    T execute();
  }

  Counter() {
    lock = new ReentrantLock();
  }

  int next() {
    lock.lock();
    try {
      Operation<Integer> operation = () -> { return count++; };
      return operation.execute();
    } finally {
      lock.unlock();
    }
  }
}

In the following class extracting step the Synchronizer type is introduced to serve as an executor that ensures a given Operation is performed within proper synchronization boundaries:

class Counter {

  private final Synchronizer synchronizer;

  private int count;

  interface Operation<T> {
    T execute();
  }

  static class Synchronizer {

    private final Lock lock;

    Synchronizer() {
      lock = new ReentrantLock();
    }

    private int execute( Operation<Integer> operation ) {
      lock.lock();
      try {
        return operation.execute();
      } finally {
        lock.unlock();
      }
    }
  }

  Counter() {
    synchronizer = new Synchronizer();
  }

  int next() {
    return synchronizer.execute( () -> { return count++; } );
  }
}

If I am not completely mistaken this should do the same as the initial class. Well, the tests were green, but plain JUnit tests do usually not help much regarding concurrency. But with a last change it is at least possible to verify the proper invocation sequence by a unit test to ensure synchronization:

public class Counter {

  final Synchronizer<Integer> synchronizer;
  final Operation<Integer> incrementer;

  private int count;

  public Counter( Synchronizer<Integer> synchronizer ) {
    this.synchronizer = synchronizer;
    this.incrementer = () -> { return count++; };
  }

  public int next() {
    return synchronizer.execute( incrementer );
  }
}

As you can see the Operation and Synchronizer have been moved to their own files. This way the synchronization aspect is provided and can be tested as a seperate unit. The Counter class now uses the constructor to inject a synchronizer instance4. Furthermore the incrementation operation has been assigned to a field named ‘incrementer’. To ease testing a bit the final fields’ visibility has been opened to default. A test using Mockito for e.g. spying on the synchronizer could now ensure the proper synchronization call like this:

@Test
public void synchronization() {
    Synchronizer<Integer> synchronizer = spy( new Synchronizer<>() );
    Counter counter = new Counter( synchronizer );

    counter.next();

    verify( synchronizer ).execute( counter.incrementer );
  }

Usually I am not overly exited about using method invocation verification, as this generates a very tight coupling between unit and test case. But given the circumstances above, it does not look as a too bad compromise to me. However I am just doing first warmups with Java 8 and Lambda expressions and maybe I am missing something on the concurrency side too – so what do you think?

  1. ReentrantLock Example in Java, Difference between synchronized vs ReentrantLock, Javin Paul, March 7, 2013
  2. Obviously enough noise to confuse me, because my first test version failed…
  3. I decided to go with a type parameter return value instead of int. This way the resulting synchronization mechanism can be better reused. But I am not sure if e.g. autoboxing is uncritical here due to performance or whatsoever reasons. So for a general approach there are probably some more things to consider, which are out of the scope of this post, though
  4. If changing the constructor is for any reason not possible one might introduce a delegating default constructor that injects the new instance of Synchronizer into the parameterized one like this: this( new Synchronizer() );. This approach might be an acceptable overhead for testing purpose
Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You'll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You'll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


+ five = 9



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books