Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: removed all other unnecessary fields; current code is enough to demonstrate the problem

...

Code Block
bgColor#FFcccc
public final class Lazy {
  private static int number;
  private static Connection conn;
	  
  static {
    Thread t = new Thread(new Runnable() {
      public void run() {
        // Initialize, a database connection
        try {
	  conn = DriverManager.getConnection("connectionstring");	 
  	} catch (SQLException e) {
	  conn = null;	
	}
      }
    });
	    
    t.start();
    try {
      t.join();
    } catch(InterruptedException ie) {
      throw new AssertionError(ie);
    }
    // Other initialization
    number = 42;
  }
	  
  public static Connection getConnection() {
    if(conn == null) {
      throw new IllegalStateException("Connection not initialized");  
    }
    return conn;
  }
	  
  public static void main(String[] args) {
    // System.out.println(number...
    Connection connection = Lazy.getConnection();
  }
}

The code in the static block is responsible for initialization, and starts a background thread. The background thread attempts to initialize a database connection and also assigns to the field number but needs to wait until initialization of all members of the Lazy class has finished.

...

Code Block
bgColor#ccccff
public final class Lazy {
  private static int number;
  private static Connection conn;

  static {
    // Initialize, a database connection
    try {
      conn = DriverManager.getConnection("connectionstring");
    } catch (SQLException e) {
      conn = null;	
    }        
    // Other initialization
    number = 42;
  }

  // ...
}

Compliant Solution (ThreadLocal)

This compliant solution uses a ThreadLocal object to initialize a the database connection and sets flag to true depending on whether the initialization succeeds. The number field is initialized independently in a static initializer.

Code Block
bgColor#ccccff
public final class Lazy {
  private static boolean flag;
  private static int number;

  static {
    number = 42;   
  }

  private static final ThreadLocal<Connection> connectionHolder
    = new ThreadLocal<Connection>() {
      public Connection initialValue() {
        try {
          Connection conn = DriverManager.getConnection("connectionstring");
          flag = true;
          return conn;
        } catch (SQLException e) {
          flag = false;
          return null;
        }
      }
    };
    
  public static Connection getConnection() {
    Connection connection return= connectionHolder.get();
    if(connection == null) {
      throw new IllegalStateException("Connection not initialized");  
    }
    return connection;
  }

  public static void main(String[] args) {
    // ...
    Connection conn = getConnection();
    System.out.println(flag);
  }
}

It is also safe to set other shared class variables from the initialValue() method. Consequently, each thread will see a consistent value of the flag field.

Exceptions

Wiki Markup
*CON03:EX1*: The {{ObjectPreserver}} class (based on \[[Patterns 02|AA. Java References#Patterns 02]\]) is shown below: This class provides a mechanism for storing object references, which prevents an object from being garbage-collected, even if the remaining program no longer maintains a reference to the object.

...