Throughout this chapter on synchronization, we kept
referring to “obtaining the object lock.” But what about
static methods?
When a synchronized static method is called, which object
are we referring to? A static method does not have a
concept of the this
reference. It is not possible
to obtain the object lock of an object that does not exist. So how
does synchronization of static methods work? To answer this question,
we will introduce the concept of a class lock. Just as there is an
object lock that can be obtained for each instance of a class
(object), there is a lock that can be obtained for each class. We
will refer to this as the class lock
. In terms of
implementation, there is no such thing as a class lock, but it is a
useful concept to help us understand how this all works.
When a static synchronized method is called, the program obtains the class lock before calling the method. This mechanism is identical to the case in which the method is not static; it is just a different lock. The same rule applies: if a synchronized static method calls another synchronized static method of the same class, the system is smart enough to support the nesting of class locks.
But how is the class lock related to the object lock? Apart from the functional relationship between the two locks, they are not operationally related at all. These are two distinct locks. The class lock can be grabbed and released independently of the object lock. If a nonstatic synchronized method calls a static synchronized method, it acquires both locks. Achieving deadlock between these two locks is a little difficult (but not impossible) to accomplish since a static method cannot call a nonstatic method without an object reference.
If a synchronized static method has access to an object reference, can it call synchronized methods of that object or use the object to lock a synchronized block? Yes: in this case the program first acquires the class lock when it calls the synchronized static method and then acquires the object lock of the particular object:
public class MyStatic { public synchronized static void staticMethod(MyStatic obj) { // Class lock acquired obj.nonStaticMethod(); synchronized (obj) { // Class and object locks acquired } } public synchronized void nonStaticMethod() { // Object lock acquired } }
Can a nonstatic method grab the static lock without calling a synchronized static method? In other words, can a synchronized block apply to the class lock? For example, something like this:
public class ClassExample { synchronized void process() { synchronized (the class lock) { // Code to access static variables of the class } } }
The main reason for a nonstatic method to grab a class lock is to prevent a race condition for variables that apply to the class (i.e., static variables). This can be accomplished by calling a static synchronized method of the class. If for some reason this is not desired, we can also use the synchronized block mechanism on a common static object (using a static instance variable would probably be the best technique for storing such a common object). For example, we could use an object stored in a common location that can be accessed by all objects of a particular class type:
public class ClassExample { private static Object lockObject = new Object(); synchronized void process() { synchronized (lockObject) { // Code to access static variables of the class } } }
Finally, if
creating a new object is not desired, you may also obtain the class
object (that is, the instance of the
java.lang.Class
class) that represents the class
itself. Objects of this class are used to represent classes in the
system. For our purposes, we are using this class because there is a
one-to-one ratio of classes and objects of the Class class that
represents the classes. This class object can be obtained as follows:
public class ClassExample { synchronized void process() { synchronized (Class.forName("ClassExample")) { // Code to access static variables of the class } } }
A call to the forName()
method of the Class
class returns this object. We can then use this class object as the
locking object via the synchronized block
mechanism.