Lesson 13 | Perfect Singleton Design Pattern |
Objective | What does it take to make the Singleton Design Pattern perfect |
Perfect Singleton Design Pattern
Question: When should one use the "synchronized" keyword in combination with the Singleton Design Pattern?
The synchronized keyword in Java is used to control access to critical sections of code by multiple threads. In the context of the Singleton design pattern, synchronized is used to prevent multiple threads from creating more than one instance of the singleton class.
In a multithreaded environment, it's possible that two or more threads will evaluate the singleton instance check if (instance == null) at the same time and find it to be true. As a result, each of these threads will create a new instance of the singleton, which violates the pattern's intent to allow exactly one instance. Here's a simple Singleton implementation using synchronized:
public class Singleton {
private static Singleton instance;
private Singleton() { }
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
In the getInstance() method, synchronized is used to ensure that only one thread at a time can execute this method. As a result, only one instance of the Singleton class is created, even in a multithreaded environment.
However, using synchronized for the entire getInstance() method can have performance implications, as every call to getInstance() requires synchronization, even after the instance has been initialized.
To avoid this, you can use the "double-checked locking" idiom:
public class Singleton {
private volatile static Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
In this case, synchronization is only used during the first time that the instance is created. The volatile keyword is used to ensure that changes to the instance variable are immediately made visible to all threads. Keep in mind that even though these examples solve the problem of instance creation in multithreaded environments, they still represent a global state in your application, and the usual considerations and design trade-offs concerning global state apply.
Given :
11.class ChocolateBoiler {
12. private static ChocolateBoiler boiler;
13. private ChocolateBoiler() { }
14. public static final ChocolateBoiler getInstance() {
15. if(boiler == null) {
16. boiler = new ChocolateBoiler();
17. }
18. return boiler;
19.}
The above code shows a class depicting the Singleton design pattern. What can be done to make it a perfect Singleton design pattern?
Please select 1 option:
- Remove the if statement on line 15
- The given code is already a perfect singleton design pattern.
- Mark the static boiler instance variable as final.
- Synchronize the getInstance method using synchronized keyword.
Answer : D
Explanation:
In this code fragment, there is a
class
ChocolateBoiler which has a
- static final method getInstance and
- a static instance variable boiler.
The default constructor of the ChocolateBoiler class is marked as private.
The constructor of this class cannot be called from an outer class because it is private. This prevents the instantiation of the class from an outer class.
Hence, only a static method inside the class can access the constructor of the class.
The getInstance method serves this purpose and when the getInstance method is invoked, it checks for the boiler variable to be null.
If yes, it instantiates the boiler variable, else it returns the object which is already referenced by the boiler variable.
Hence, it is verified that there is
one and only one object that is created and returned when asked for.
This type of design pattern is called the Singleton design pattern in Java.
However, when
multithreading comes into the picture, if two or more threads somehow enter the "if block" in the getInstance method,
there will be two instances of the ChocolateBoiler which is contradictory to the Singleton pattern.
So marking the getInstance method as
synchronized ensures thread safety because only one thread will be able to enter the method at a time.
//According to the Oracle docs, the synchronized keyword should appear after the public keyword
// https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
class PerfectChocolateBoiler {
private static PerfectChocolateBoiler boiler;
private PerfectChocolateBoiler() { }
public synchronized static final
PerfectChocolateBoiler getInstance() {
if (boiler == null) {
boiler = new PerfectChocolateBoiler();
}
return boiler;
}
public static void main(String[] args) {
// more code here
}
}