Home > Java > 3, 2, 1, Go! A CountdownLatch in practice

3, 2, 1, Go! A CountdownLatch in practice

Recently, I attended a talk titled ‘the Well-grounded Java Developer’, at Devoxx ’11. The authors of the similarly named book convinced me once more that the java.util.concurrent package in the JDK contains a lot of useful classes and concepts that deserve a closer look. The package should definitely keep you from re-inventing the wheel.

Thus, it reminded me of how we put a CountdownLatch to actual use in a recent project.
In this post we will show you how.

Of course we could start with re-stating the already excellently written javadoc of the CountdownLatch class but we won’t. Just read it yourself, before continuing this post or afterwards.

The case

In our case, we had to unit test a utility method that generates unique session id’s.
But actually, it turned out that they weren’t as unique as we expected :-(

Apparently, it wasn’t thread safe. So, I decided to fight the bug test driven:
1. Write a unit test first, that proves the current version is broken
1. Look at the code and solve it theoretically.
2. Then run the test again to prove the new version is, well, better.

The utility class looks like this in condensed form:

@NotThreadSafe
public class Util {

    private static final Base64 base64encoder = new Base64(true);

    public String uuid() {
        UUID uuid;

		synchronized {
			uuid = UUID.randomUUID();
        }
        return base64encoder.encodeToString(asByteArray(uuid)).trim();
    }
}

This creates a unique uuid, but it is broken (hence the @NotThreadSafe annotation, a habit suggested by Brian Goetz in Java Concurrency in Practice).
To prove this brokenness, we will now create a unit test which uses CountdownLatch to setup a very multithreaded use of this uuid-method.

Why CountdownLatch

To create a setup that gets multiple threads to concurrently invoke the method under test, one could write (in pseudo code):

for (i = 1..100) {
	new Thread(new Runnable() {
		start() {
			Util.uuid();
		}
	}).run();
}

This creates 100 threads, and runs them.
But wait… how concurrent is this actually? Won’t creating, starting and stopping the thread outweigh the time of actually invoking Util.uuid()?
So, a better setup would be (in pseudo code):

for (i = 1..100) {
	new Thread(new Runnable() {
		start() {
			waitForSignal();
			Util.uuid();
		}
	})
}
allThreads.signal();

This would create all threads, start them, but wait for a final signal to actually invoke the method under test.

Enter CountdownLatch.

Although this signalling could be manually done by Object.notify() and Object.wait(), CountdownLatch just adds some sugar to make this signalling readable and understandable.
Besides, the name ‘latch’ is just appealing for such a use case.

So, the unit test looks more or less like this:

CountDownLatch startGate = new CountDownLatch(101);
CountDownLatch endGate = new CountDownLatch(100);
for (int j = 0; j < 100; j++) {
	new Thread(new Runnable() {
		public void run() {
			try {
                startGate.countDown();
				startGate.await();
				System.out.println(Util.uuid());
			} catch (InterruptedException e1) {
				Sytem.err.println("Exception occurred: " + e1.getMessage());
			} finally {
				endGate.countDown();
			}
		}
	}).start();
}
startGate.countDown();
endGate.await();
}

It starts 100 threads, sets them on hold (in front of the ‘startGate’). When all threads are lined up, they are released and will hit Util.uuid() simultaneously.
This should look familiar if you read the javadoc of CountdownLatch. Refer to the complete unit test to see the verification of duplicate uuid’s, some more debugging information, each thread looping 100 times, etc.

My JUnit-output says:

junit.framework.AssertionFailedError: Unexpected amount of unique ID's generated, expected: but was:
	at nl.iprofs.blog.uuid.UUIDSessionIdGeneratorTest.uniqueId(UUIDSessionIdGeneratorTest.java:74)

That’s pretty bad…

The bug fix

Well, the point of this blog post is not the bug itself (which was the fact that org.apache.commons.codec.binary.Base64 is not thread safe) so I will not linger too long there.
Let it suffice that creating a new Base64() for every invocation fixed it.
Afterwards, re-running the unit test succeeds, proving the fix.

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 )

Connecting to %s

Follow

Get every new post delivered to your Inbox.