...
In this noncompliant code example, the statements in the static
initializer start a background thread as part of class initialization. The background thread attempts to initialize a database connection but needs to wait until initialization of all members of the Lazy
ConnectionFactory
class, including dbConnection
, has finished.
Code Block | ||
---|---|---|
| ||
public final class LazyConnectionFactory { private static Connection dbConnection; // Other fields ... static { Thread dbInitializerThread = new Thread(new Runnable() { public void run() { // Initialize the database connection try { dbConnection = DriverManager.getConnection("connection string"); } catch (SQLException e) { dbConnection = null; } } }); // Other initialization, for example, start other threads dbInitializerThread.start(); try { dbInitializerThread.join(); } catch(InterruptedException ie) { throw new AssertionError(ie); } } public static Connection getConnection() { if(dbConnection == null) { throw new IllegalStateException("Error initializing connection"); } return dbConnection; } public static void main(String[] args) { // ... Connection connection = getConnection(); } } |
Wiki Markup |
---|
Statically-initialized fields are guaranteed to be fully constructed before they are made visible to other threads (see [CON26-J. Do not publish partially initialized objects] for more information). Consequently, the background thread must wait for the main (or foreground) thread to finish initialization before it can proceed. However, the {{LazyConnectionFactory}} class's main thread invokes the {{join()}} method which waits for the background thread to finish. This interdependency causes a class initialization cycle that results in a deadlock situation. \[[Bloch 05b|AA. Java References#Bloch 05b]\] |
...
Code Block | ||
---|---|---|
| ||
public final class LazyConnectionFactory { private static Connection dbConnection; // Other fields ... static { // Initialize a database connection try { dbConnection = DriverManager.getConnection("connection string"); } catch (SQLException e) { dbConnection = null; } // Other initialization (do not start any threads) } // ... } |
...
Code Block | ||
---|---|---|
| ||
public final class LazyConnectionFactory { private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { public Connection initialValue() { try { Connection dbConnection = DriverManager.getConnection("connection string"); return dbConnection; } catch (SQLException e) { return null; } } }; // Other fields ... static { // Other initialization (do not start any threads) } public static Connection getConnection() { Connection connection = connectionHolder.get(); if(connection == null) { throw new IllegalStateException("Error initializing connection"); } return connection; } public static void main(String[] args) { // ... Connection connection = getConnection(); } } |
...
This is a singleton class (see CON23-J. Address the shortcomings of the Singleton design pattern for more information on how to defensively code singleton classes). The initialization creates involves creating a background thread using the current instance of the class. The thread waits indefinitely by invoking Object.wait()
. Consequently, this object exists for the remainder of the JVM's lifetime. Because the object is managed by a daemon thread, the thread does not hinder a normal shutdown of the JVM.
...