The Treacherous Paths to Concurrency

A quick look at the Activity Monitor on my system shows that while one core is super busy running the code, the other cores are sitting around sipping iced tea. Since this program is I/O intensive, we can get better performance if we throw a few threads at the problem and make use of those other cores.

Put on your Java hat for a minute and think about ways to make the code faster. The mere thought of using the JDK concurrency library can be quite nerve-wracking. Starting threads is not really hard, only clumsy—you’d use the executors to create a pool for threads. You can schedule exploring different subdirectories to threads from the pool. But the problem stems from the two variables—the shared mutable variables. When multiple threads get the children directories, we have to update the count and the filesToVisit variables. Let’s see why this would be a problem:

  • To protect the count variable from concurrent change, we may use AtomicLong. This is problematic. We have to ensure that all changes to this variable have happened before the program sees there are no more files to visit and reports the count. In other words, although atomic guarantees thread safety for a single value, it doesn’t guarantee atomicity across multiple values that may potentially change at the same time.

  • We would have to use a thread-safe collection, a synchronous list or a concurrent list, to implement the filesToVisit list. This again will protect that one variable but does not address the issue of atomicity across two variables.

  • We could wrap the two variables in a class and provide synchronized methods to update the two values in one shot. That will take care of ensuring the changes to the two variables are atomic. However, now we have to ensure that the synchronization is actually happening at the right place, for the right duration. If we forget to synchronize or synchronize at the wrong place, neither the Java compiler nor the runtime will give us any warnings.

In short, the change from sequential to concurrent often turns the code into a beast. The more mutable variables, the messier it gets and the harder it becomes to prove the correctness of the code. There’s no point in writing code that runs really fast just to produce unpredictable errors.

The problem on hand is a great candidate to apply actors. We can decompose the problem into subtasks, using the divide-and-conquer approach. The mutable variables can be tucked away into an actor to prevent multiple threads from concurrently updating them. Change requests can be queued instead of threads blocking and waiting on each other. We’ll implement the program using actors soon, but first let’s work through a few examples to learn about actors and how to use them.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.28.94