Monday, March 31, 2008

Immutable locks

Immutability is something that's mentioned over and over when it comes to parallel programming. Part I of Java Concurrency In Practice is all about composing objects that "play well" in the concurrent world and those that are immutable are the "best" citizens. No wonder functional languages are coming up in a big way. Anyone want to bet on them taking over the world in the next decade? Or if not taking over outright, at least succeeding in mutating our beloved imperative ones into unrecognisable functional beasts.

Anyway, check out the method below:

public class Foo {
    private Listener[] listeners;

    public Foo() {
        listeners = new Listener[0];
    }

    public void addListener(Listener listener) {
        synchronized (listeners) {
            Listener[] newListeners = new Listener[listeners.length + 1];

            for (int i = 0; i < listeners.length; ++i) {
                newListeners[i] = listeners[i];
            }

            newListeners[listeners.length] = listener;

            listeners = newListeners;
        }
    }
}


So here, the first thread to obtain the lock on listeners reassigns it to a new Array object... newListeners. Subsequent threads would continue to lock on the old listeners array, while new threads would lock on the "new" listeners array, and potentially corrupt the data. So I guess there's an unwritten property about locks... they need to be immutable to avoid situations like the above.

So given that locks need to be constant, there's no way to have a workable solution in the above code without using an additional object as the lock. The easy "lazy" thing to do is to just synchronize the method itself. The Foo instance (this) would then be that "additional object". But that would be wasteful since it would prevent all other synchronized method calls, even ones that have nothing to do with listeners.

(If instead of an Array, a List was used, the problem wouldn't have occurred given that the List itself wouldn't change. And I think, nowadays mostly the thinking when programming in managed languages is to go for Collections, rather than mess with manually managing Arrays. But I have still seen code like the above, so it's not totally contrived.)

No comments: