Though concurrency is not really a hobby of mine, but I do find it interesting, and once in a while, it gets me intrigued. This article is about locking, and choosing the right type of lock and what the consequences are when picking the wrong lock!
A simple example program can be found below, which uses a separated lock object to lock on:
public class Worker { private final Object lock = new Object(); public void work() { synchronized(lock) { // some work takes a long time } } }
The above code describes a safe way of instance based locking. Ofcourse, we can use any type of object to lock on, we don’t necessarily have to use Object for that. Or do we?
What if we, for example, are not completly satistied with the Object lock? I mean: it doesn’t provide much information and it’s not really flexible, is it? Let say I’d like to give my lock a name, and, since I don’t need anything else than a name for my lock, why not use a simple String object?
public class Worker { private final String lock = "A rusty old lock"; public void work() { synchronized(lock) { println lock; // some work takes a long time } } }
Done! We now have a nice, private and final (to prevent reordering and visibility problems) instance based String lock, which is being printed whenever we call the ‘work’ method. Hmm, well, wait… wasn’t there some performance optimalization called String Interning? When the when the VM runs our application, our String is added to the String pool. This means, that only one (1!) instance of our lock is present in our VM, instead of a lock per instance. So, is that instead of locking on an instance based lock, we are instead locking at a class level and this can cause some unexpected deadlocks or slowdowns in hour application.
Note that since autoboxing, which was introduced in Java 5, this is also the case for numbers, but not for all of them. The above is only valid for numbers in the [-127, 128] range. These numbers are retreived from an object pool, and are cached for performance reasons. So, let’s change the example above to use a Number lock instead of a String.
public class Worker { private final Number lock = 4; public void work() { synchronized(lock) { println lock; // some work takes a long time } } }
The above code will have the same problem as the String lock: instead of having an instance based lock, you will have a class based lock, which is probably not what you anticipated for!
Conclusion
In short: be aware of the JVM internals. Try to be aware of any side effects your code might have, and try to test as much as possible. As a guideline, I would suggest sticking to the first code example: use an Object for your lock, you can’t go wrong there!