Use the
Singleton1 form when you cannot create the object at class-load time (for example, you did not have information determined by program state or is passed to the creation method).
You must synchronize the instance() method of Singleton1 as shown.
Use the
Singleton2 or
Singleton3 form when possible; synchronization is not required during access.
(The JVM may load the class at any time, but it should not initialize the Class object until first use )c
Java Language Specification for Java SE 22 static initializers should not execute until first use.c
Call
addShutdownHook()
in the constructor when program-shut-down cleanup activities (such as shutting down database connections in an orderly way) are required.
Do not use a finalizer, which may never be called. A private constructor prevents someone from saying new Singleton(), thereby forcing access through instance().You have no requirement that only one instance of the Singleton exists, only that the number of instances are constrained and that access to the instances are global. For example, a
DatabaseConnection.getInstance()
method may return one of a pool of database connections that the Singleton manages.
In UML, the role associated with the Singleton is usually also the class name.
Code Analysis:
-
Class Name:
- The class is named
Singleton1
, which indicates it is meant to implement the Singleton design pattern.
- However, the name of the class itself is inconsistent with the instance variable
Singleton
, suggesting some confusion in naming.
-
Instance Variable:
private static Singleton instance;
:
- This is the static instance of the Singleton class, ensuring a single instance across the application.
- There’s a mismatch between the declared type (
Singleton
) and the class name (Singleton1
). This would cause a compilation error unless Singleton
is an external class.
-
Constructor:
private Singleton1()
:
- The constructor is private, which correctly prevents instantiation from outside the class.
- Inside the constructor, a shutdown hook is added using
Runtime.getRuntime().addShutdownHook
.
- This shutdown hook registers a thread to perform cleanup when the JVM shuts down, ensuring resource cleanup is handled gracefully.
-
Instance Method:
public static synchronized Singleton instance()
:
- This method ensures lazy initialization of the singleton instance.
synchronized
ensures thread safety, but it may lead to performance bottlenecks under heavy contention.
- It checks if the instance is
null
and creates it if it hasn't been initialized, following the "lazy initialization" approach.
- Again, the mismatch in naming (
Singleton1
vs. Singleton
) would cause issues here.
-
Potential Issues and Improvements:
- Naming Inconsistency:
- The class is named
Singleton1
, but the instance variable and method reference a type Singleton
, which leads to confusion and potential compilation errors.
- Solution: Rename the class or correct the instance variable and method return type to match the class name.
- Thread-Safety Concerns:
- The use of
synchronized
ensures thread safety but is less performant. A better approach would be the double-checked locking pattern or using an enum
for Singleton.
- Early Resource Allocation:
- The shutdown hook is initialized in the constructor, which might not be desirable for all use cases. It could be deferred until necessary.
- Refactored Code with Improvements:
class Singleton1 {
private static Singleton1 instance;
private Singleton1() {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// Clean-up code here
}));
}
public static Singleton1 getInstance() {
if (instance == null) {
synchronized (Singleton1.class) {
if (instance == null) {
instance = new Singleton1();
}
}
}
return instance;
}
}
Key Changes:
- Corrected the class name and instance variable type.
- Implemented double-checked locking for better performance in a multithreaded environment.
- Used a lambda for the shutdown hook for cleaner code (requires Java 8+).
Summary:
The original code implements a lazy-initialized, thread-safe Singleton, but with naming inconsistencies and potential performance bottlenecks. The refactored version addresses these issues while preserving the functionality and improving efficiency.
package com.gofpatterns.creational.singleton;
class Singleton2{
private static final Singleton instance=new Singleton2();
public static Singleton instance(){
return instance;
}
/*Other than creating object in static
initializer, is identical to Singleton1*/
}