Testing Multithreaded Code in Java

DZone's Guide to

Testing Multithreaded Code in Java

Testing multithreaded code is difficult to do. Usually, you need to configure timers, shared booleans, state across threads, and other difficult to manage things. In this article, Felipe shows us how to test multithreaded java code without the hassle.

Testing multithreaded code is a tough challenge. The first advice that you get when trying to test concurrency is to isolate your concurrent concerns in the code as much as possible. This a general design advice but in this case it's even more important. Make sure to first properly unit test the logic that is wrapped by the concurrent construct. Otherwise you might spend a long time trying to figure out a concurrency problem that turns out to be flawed business logic in the end.

Once you have got that covered, you can think about your strategy to test concurrent systems. GOOS covers how you can do it. Here you can find the code that I'm going to explain:

As you can see, this class is not thread-safe, as it's exposing some state through the inc() method. The state is not thread-safe (you could use AtomicInteger instead of BigInteger to fix that). To test that class we'll include a non-concurrent and a concurrent test.

If you execute that test you will receive different results, and sometimes it's even passing! That's because this test is not deterministic; we can't assure how the threads will interleave in every execution. If we want to be as sure as possible that this test finds the possible bug, we should increase the number of threads and iterations, but with the obvious time trade-off.

You can use a more deterministic approach using Weaver. To understand how it works, let's illustrate it with an example. Let's say that we have an in-memory and not thread-safe store:

That service is a singleton living in a server that spawns a thread per request, so we'd like to execute that piece atomically. We could use the stress test non-deterministic approach or we could use Weaver. If we think deeply about this problem, we realize we want to test every combination of the following (as an example, Thread 1 executes line 1 in moment x and Thread 2 executes line 1 in moment x, would be -> T1/1 : T2/1)

T1/1 : T2/1

T1/1 : T2/2

T1/1 : T2/3

....

T1/2 : T2/1

T1/2 : T2/2

T1/2 : T2/3

....

For instance, we'll have a problem if T1/5 and T2/2, as T1 didn't save yet, and T2 has already got an empty score from store. That means that T1 will save a score in a level and then T2 will do the same, breaking the logic. And that's exactly what Weaver does, it grabs a method and executes the above combinations using two threads.

If I get rid of the preparation code (annotated with @ThreadedBefore), the test code will look like this:

This test will always fail, as it is deterministic. As you can see, testing concurrency is quite hard, and that's why I'm a supporter of modern frameworks that try to hide that hassle into a platform or overcome the problem through immutable data. You can read more about it here.