Programming Java threads in the real world, Part 7

Singletons, critical sections, and reader/writer locks

1 2 3 4 Page 2
Page 2 of 4

The static synchronized method forms a critical section -- a block of code that can be executed by only one thread at a time. If get_instance() weren't synchronized, a thread could be preempted after the if statement was processed, but before the instance=new Singleton() was executed. The preempting thread could then call get_instance(), create an instance, and yield. The preempted thread would then wake up, think that there were no instances (because it has already performed the test), and create a second instance of the object. The "critical section" eliminates the multiple-creation problem by preventing any thread from entering get_instance() if any other thread is already inside the method. Any singleton object can be used to implement a critical section. Here, the Class object whose monitor we're using is itself a singleton, so by locking this object implicitly when we enter the static method, we prevent other threads from executing the method in parallel. (All synchronized static methods actually are critical sections when you look at them that way.)

This strategy of using the Class object's monitor as the critical-section lock doesn't always work out because you lock all the static methods of the class, not just the singleton-creation method. You can do the same thing with an explicitly declared singleton lock as follows:

private static Object lock = new Object();
public static Singleton get_instance()  // not synchronized
{   synchronized( lock )
    {   if( instance == null )
            instance =  new Singleton();
        return instance;
    }
}

This version still assures that only one instance of the singleton will be created, but it won't interfere with the execution of other static methods.

The main problem with this naive approach is efficiency. We acquire the lock every time we call get_instance(), even though the code only needs to be locked the first time the method is called. The solution to this problem is Doug Schmidt's "double-checked locking" strategy. Here's the general pattern:

class Singleton
{
   private Singleton instance;
    //...
   public static Singleton get_instance()  // not synchronized
    {   if( instance == null )
        {   synchronized( Std.class )
            {   if( instance == null )
                    instance =  new Singleton();
            }
        }
        return instance;
    }
}

Most of the time, the object will exist when get_instance() is called, so we won't do any synchronization at all. On the first call, however, instance is null, so we enter the if statement and synchronize explicitly on the Class object to enter a critical section. Now we have to test for instance==null again, because we might have been preempted just after the first if was processed but before the synchronized statement was executed. If instance is still null, then no other thread will be creating the singleton, and we can create the object safely.

Listing 1 shows you a real-world application of a singleton that compensates for a problem in the design of the System class. A proper OO design never uses public fields except for symbolic constants, and I really mean "constant" here: The exposed field must be immutable, not just final. (An object accessed via a final reference can be modified; an "immutable" object (like a String) can't be modified at all.) This rule applies to both "class" and "instance" variables, and there are no exceptions to this rule. Ever. Period. Strong encapsulation of an object's implementation is so central to what "object orientation" means, that this point is simply not negotiable. If you use public fields, your program just isn't object oriented -- it's some sort of part-OO/part-procedural polyglot, and you will reap virtually none of the real benefits of OO such as improved maintenance. The only legitimate public members of a class are those methods that handle messages defined in your design's dynamic-model.

The foregoing notwithstanding, there is one place in the Java packages where instance variables are exposed: System.in, System.out, and System.err. To my mind, this exposure is a serious design flaw: These fields are not Reader or Writer derivatives, so they are not internationalizable. Consequently, you can't use these variables without wrapping them in a Reader or Writer. If System.in, System.out, and System.err had been accessed through "accessor" methods rather than directly, this wrapping could have been done transparently by the (missing) method that returned the I/O stream. This method could have easily been modified to return a PrintWriter rather than a PrintStream without impacting much of the code that used it. As it is, there's a lot of incorrect code out there that uses the three streams directly.

Listing 1 solves the problem (or at least hides it) by using the Singleton pattern. You write to standard output, for example, like this: Std.out().println("Hello world"); The out() method (Listing 1, line 33) creates a singleton PrintWriter wrapper around System.out and returns it. Subsequent calls to Std.out() return the same wrapper object, so you don't have to create a new one every time you need to write a string.

Other methods in the class work the same way: Std.err() returns a singleton PrintWriter that wraps System.err, and Std.in() returns a BufferedReader that wraps System.in. I've also provided a Std.bit_bucket() that returns an implementation of PrintWriter that does nothing. This is occasionally useful for throwing away otherwise undesirable output. For example, you might pass a method a Writer onto which it prints error or status messages. Passing this method Std.bit_bucket() causes the messages to not be printed.

Note, by the way, that the Bit_bucket class (Listing 1, line 61) is private, but it extends PrintWriter -- a public class -- overriding all the methods with no-ops. This notion of a private class implementing a public interface is a useful one. The outside world sees a Bit_bucket object as a Print_writer, knowing nothing about its actual implementation -- not even its class name. Though it doesn't do it here, the private inner class can define a set of methods that comprise a private interface to the outer class. This way the outer-class object can communicate with the inner-class object using methods that nobody else can access.

Listing 1: /src/com/holub/tools/Std.java
001  
002  
003  
004  
package com.holub.tools;
import java.io.*;
import com.holub.asynch.JDK_11_unloading_bug_fix;
/** A convenience class that takes care of wrapping a writer around
 |    standard output.
 */
005  
006  
007  
008  
009  
010  
011  
012  
013  
014  
public final class Std
{
   static{ new JDK_11_unloading_bug_fix(Std.class); }
   private static BufferedReader input;        //= null
   private static PrintWriter    output;       //= null
   private static PrintWriter    error;        //= null
   private static PrintWriter    bit_bucket;   //= null
    /*******************************************************************
     |    A private constructor, prevents anyone from manufacturing an instance.
     */
015  
016  
  private Std(){}
    /*******************************************************************
     |    Get a BufferedReader that wraps System.in
     */
017  
018  
019  
020  
021  
022  
023  
024  
025  
026  
027  
028  
029  
030  
031  
032  
  public static BufferedReader in()
    {
        if( input == null )
            synchronized( Std.class )
            {   if( input == null )
                    try
                    {   input = new BufferedReader(
                                        new InputStreamReader(System.in));
                    }
                    catch( Exception e )
                    {   throw new Error( e.getMessage() );
                    }
            }
        return input;
    }
    /*******************************************************************
     |    Get a PrintWriter that wraps System.out.
     */
033  
034  
035  
036  
037  
038  
039  
040  
041  
  public static PrintWriter out()
    {   if( output == null )
            synchronized( Std.class )
            {   if( output == null )
                    output = new PrintWriter( System.out, true );
            }
        return output;
    }
    /*******************************************************************
     |    Get a PrintWriter that wraps System.err.
     */
042  
043  
044  
045  
046  
047  
048  
049  
050  
051  
  public static PrintWriter err()
    {   if( error == null )
            synchronized( Std.class )
            {   if( error == null )
                    error = new PrintWriter( System.err, true );
            }
        return error;
    }
    /*******************************************************************
     |    Get an output stream that just discards the characters that are sent to it.
     */
052  
053  
054  
055  
056  
057  
058  
059  
060  
  public static PrintWriter bit_bucket()
    {   if( bit_bucket == null )
            synchronized( Std.class )
            {   if( bit_bucket == null )
                    bit_bucket = new Bit_bucket();
            }
        return bit_bucket;
    }
    /**
     |    The Bit_bucket class overrides all methods of PrintWriter to do nothing.
     */
061  
062  
063  
064  
065  
066  
067  
068  
069  
070  
071  
072  
073  
074  
075  
076  
077  
078  
079  
080  
081  
082  
083  
084  
085  
086  
087  
088  
089  
090  
091  
092  
093  
094  
095  
096  
097  
098  
099  
100  
101  
102  
103  
104  
105  
  private static final class Bit_bucket extends PrintWriter
    {
       private Bit_bucket()
        {   super( System.err );    // have to pass it something legal. Is never used.
        }
       public void    close()                              {}
       public void    flush()                              {}
       public void    print(boolean b)                     {}
       public void    print(char c)                        {}
       public void    print(char[] s)                      {}
       public void    print(double d)                      {}
       public void    print(float f)                       {}
       public void    print(int i)                         {}
       public void    print(long l)                        {}
       public void    print(Object o)                      {}
       public void    print(String  s)                     {}
       public void    println()                            {}
       public void    println(boolean b)                   {}
       public void    println(char c)                      {}
       public void    println(char[] s)                    {}
       public void    println(double d)                    {}
       public void    println(float f)                     {}
       public void    println(int i)                       {}
       public void    println(long l)                      {}
       public void    println(Object o)                    {}
       public void    write(char[] buf)                    {}
       public void    write(char[] buf, int off, int len)  {}
       public void    write(int c)                         {}
       public void    write(String buf)                    {}
       public void    write(String buf, int off, int len)  {}
    }
   static public class Test
    {
       static public void main( String[] args ) throws IOException
        {   String s;
            while( (s = Std.in().readLine()) != null )
            {   Std.out().println( s );
                Std.err().println( s );
                Std.bit_bucket().println( s );
            }
        }
    }
}

The final thread-related subtlety is the static initializer block (Listing 1, line 8):

    static{ new JDK_11_unloading_bug_fix(Std.class); } 

The JDK_11_unloading_bug_fix class in Listing 2 gets around a bug in the VM released with all versions of JDK 1.1. The VM in those releases was much too aggressive about unloading (and garbage collecting) Class objects: If the only reference to an object of a given class was a self-referential static member of the Class object, then the VM would unload the class from memory, thereby destroying our only copy of the singleton. The next time someone tried to get an instance, the class would be reloaded and a second instance of the singleton would be created. Sometimes this behavior did nothing but make the program a little slower. But if the act of creating the singleton object has side effects (like creating temporary files or opening data-base connections ), this second creation can be a problem.

The fix in Listing 2 is a kluge, but it works. I'm counting on the fact that the VM itself keeps around references to potentially active threads. If the current program is not running under a 1.1 version of the JDK System.getProperty("java.version").startsWith("1.1") ) is false, nothing at all happens. If version 1.1 is active, the JDK_11_unloading_bug_fix's constructor creates a Thread derivative whose one field holds a reference to the Class object passed in as an argument. The thread's run() method immediately suspends itself by calling wait(). Since there never will be a notify(), the thread doesn't use up any machine cycles, but since the Thread object isn't garbage collected, the Class-object reference will continue to exist, preventing the class from being unloaded. The created thread is given "daemon" status so that its existence won't stop the program from terminating when the non-daemon threads shut down.

1 2 3 4 Page 2
Page 2 of 4