Programming Java threads in the real world, Part 7

Singletons, critical sections, and reader/writer locks

This month I'm going to tie up a few synchronization-related loose ends left over from my previous Java Toolbox installments in this series. I'll start out looking at singletons, or one-of-a-kind objects. These are surprisingly difficult to implement efficiently in a multithreaded environment, but are essential in most programs. (java.awt.Toolkit is an example of a singleton.) Along the way, I'll also look at critical sections, or blocks of code -- as compared to objects -- that can be locked.

I'll finish up with a completely unrelated topic: reader/writer locks, which give you efficient, thread-safe access to read/write resources such as data structures and files. Reader/writer locks are simple enough to implement that I didn't want to devote an entire column to them, but they're essential in any multithreaded program that performs I/O operations, so I wanted to include them in the present series of articles. Reader/writer locks combined with the various semaphores and locks I've presented in previous installments of this series comprise a reasonably complete toolkit for solving thread-related synchronization problems.

Critical sections, singletons, and the Class object

So far in this series I've been concentrating on the monitor -- a means of locking an entire object while a body of code is being executed. The other essential sort of lock you should be aware of is the critical section. Critical sections are essential in implementing one-time initialization code when that code can be accessed from multiple threads.

A critical section is a chunk of code that can be executed by only one thread at a time. Compare this notion with a normal synchronized code block -- a monitor -- which is basically an exclusion semaphore that guards an entire object. Several threads can simultaneously execute a synchronized method, but only if the objects that are receiving the associated messages are different. In a critical section, the code itself is locked, not the object. Only one thread can be in the critical section at a time, even if the receiving objects are different. The mutex that guards a monitor is an object-level mutex; the mutex that guards a critical section is effectively a class-level mutex. Think of it this way: the code is defined in the class, not the object, so when you're locking the code itself, you're locking the entire class of objects. (By the way, I've seen authors get this wrong in print when they call a block of code inside a nonstatic method a "critical section." A block of code in a nonstatic method is part of the object's monitor; it is not a critical section.)

Static members

In Java, the notion of a critical section is closely tied to that of a static member, so let's start there. Java, like all OO languages, supports two categories of fields and methods:

Class variables:variables that control the state of all objects within a class.
Instance variables:variables that control the state of a single object within a class.

A class variable is implemented in Java by placing a static keyword before its definition.

To best explain how the two types of variables are used in practice, an example seems in order. Back in the dark ages (the early 1990s) somebody had the bright idea that every window on a computer screen should use a different color scheme, even within a single application. Magenta backgrounds with yellow borders, turquoise backgrounds with chartreuse borders -- it make your eyes hurt. (The reasoning was that the users would somehow remember the color combinations and more easily identify the windows. Nice theory, but the human mind just doesn't work that way.) In this system, a window's color scheme is an "instance variable": every instance -- every window -- potentially has a different value for its color scheme.

Eventually, people came to their senses and made all the windows the same color. Now the color scheme is a "class variable." The entire class of window objects uses the same color scheme. If the scheme changes, then all the windows should change their appearance.

You can model the class-level behavior like this:

class Window                    // not the AWT window
{   
   private static Color foreground = SystemColor.windowText;
   private static Color background = SystemColor.window;
synchronized static public change_color_scheme( Color foreground, Color background )
    {
        this.foreground = foreground;
        this.background = background;
        // code goes here that tells all the extant Window objects to
        // redraw themselves with the new color scheme.
    }
}

There are several problems with this simplistic approach, however, the first being threading.

Java creates a Class class object for every class in your system, and the static fields are members of this Class object. A Class object is a real object: It has methods (declared static in the class definition) and state (defined by the static fields). The Class object also has its own monitor. When you call a synchronized static method, you enter the monitor associated with the Class object. This means that no two synchronized static methods can access the static fields of the class at the same time. You can also lock the Class object explicitly, like this:

synchronized( Window.class )
{   // modify static fields here
}

Unfortunately, the Class-level monitor is in no way connected to the monitors of the various instances of the object, and a synchronized, but nonstatic, method can also access the static fields. Entering the synchronized nonstatic method does not lock the Class object. Why is this a problem? Well, in the previous example, it would appear to be harmless to omit the static (but not the synchronized) from the definition of change_color_scheme() since the static fields will be modified, even if the modifying method isn't static. Appearances are deceiving, though. If two threads simultaneously send change_color_scheme() messages to two different objects of class Window, a race condition results, and the color scheme will be in an unknown state. In other words, the individual Window objects are locked, but locking a Window object does not lock the corresponding Class object (which contains the class variables), and the static fields are unguarded. Consequently, we have two threads modifying two variables at the same time.

After threading, the second problem with the naive implementation is that there's no way to guarantee that all the existing objects stay in synch with changes to the class variables. A sloppy programmer can add an instance method (one that is not static) to the Window class, and that instance method can change the foreground or background fields without notifying the other windows, or even without updating its own color.

You can fix both the race-condition and lack-of-update problems by encapsulating the two static fields in a class of their own:

class Color_scheme
{
   private Color foreground = SystemColor.windowText;
   private Color background = SystemColor.window;
    /*package*/ synchronized change_color_scheme(
                                    Color foreground, Color background )
    {
        this.foreground = foreground;
        this.background = background;
        // code goes here that tells all the extant Window objects to
        // redraw themselves with the new color scheme.
    }
}
class Window                    // not the AWT window
{   
    static Scheme color_scheme = new Color_scheme();
    static change_color_scheme( Color foreground, Color background )
    {   scheme.change_color_scheme( foreground, background );
    }
}

Now there's no way to modify the foreground or background color without notifying the other windows. Note that this is one of the few cases in which you must use package access rather than an inner class. Had Color_scheme been an inner class of Window, direct access to foreground and background would still be possible from methods of Window. This approach also has the advantage of making the monitor that controls the Color_scheme more visible -- it's obviously the one associated with the explicit Color_scheme object, not the one associated with the Window.

Singletons

There's another problem with the earlier code, however. We really want only one Color_scheme to exist, ever. In the earlier code, I've done it accidentally by making the reference static and only calling new once, but I'd really like to guarantee that only one instance of the object can exist. The Gang of Four's (see Resources) Singleton pattern describes exactly this situation. Two excerpts from the Gang of Four book are relevant. The "Intent" section in the Gang of Four book's chapter on singletons states:

Ensure a class only has one instance, and provide a global point of access to it.

and the "Consequences" section says:

[Singleton] permits a variable number of instances. The pattern makes it easy to change your mind and allow more than one instance of the singleton class. Moreover, you can use the same approach to control the number of instances that the application uses. Only the [Instance] operation that grants access ot the singleton instance needs to change.

That excerpt from the "Consequences" section is interesting because it allows a Class object to be considered a singleton, even though there's more than one instance of the Class class in the program. It's guaranteed that there will be only a single instance of Class for a given class, so it's a singleton: Some_class.class (the "operation that grants access") always evaluates to the same Class object. The static fields and methods, since they are members of the Class object, define the state and methods of the singleton object as well. Exploiting this reasoning, I can ensure that only one instance of the Color_scheme exists by moving everything into the Class object (making everything static):

class Color_scheme
{
   private static Color foreground = SystemColor.windowText;
   private static Color background = SystemColor.window;
    private Color_scheme(){}
    /*package*/ synchronized static change_color_scheme(
                                    Color foreground, Color background )
    {
        this.foreground = foreground;
        this.background = background;
        // code goes here that tells all the extant Window objects to
        // redraw themselves with the new color scheme.
    }
}

Note that I've also added a private constructor. A class, all of whose constructors are private, can be created only by a new that's invoked in a method that legitimately has access to the class's other private components. There are no such methods here, so no instances of Color_scheme can actually be created. This guarantees that only one object can exist -- the Class object, a singleton.

I also have to change the Window to use the Class object rather than a specific instance:

class Window                    // not the AWT window
{   
    // Note that there's no field here, now.
    change_color_scheme( Color foreground, Color background )
    {   Color_scheme.change_color_scheme( foreground, background );
    }
}

I've eliminated the static field in the Window class and have invoked change_color_scheme() directly through the class.

This sort of singleton -- a class all of whose methods are static -- is called a Booch utility (after Grady Booch, who identified the pattern in one of his early books). Java's Math class is a good example of a utility-style singleton.

The problem with the make-everything-static approach to singleton creation is that all the information needed to create the object must be known at class-load time, and that isn't always possible. Java's Toolkit is a good example. An application must load a different Toolkit than an applet, but a given chunk of code doesn't know whether it's running in an application or an applet until runtime. The actual instance of the toolkit is brought into existence by calling the static method Toolkit.getDefaultToolkit(). The object itself doesn't exist until the method is called the first time. Subsequent calls return a reference to the object that's created by the first call.

Critical sections

Bringing a singleton into existence at runtime (rather than at load-time) is fraught with peril in a multithreaded environment. You can implement the creation function naively as follows:

public static synchronized Singleton get_instance()
{   if( instance == null )
        instance =  new Singleton();
    return instance;
}
1 2 3 4 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more