This chapter covers the singleton pattern.
GoF Definition
Ensure a class only has one instance, and provide a global point of access to it.
Concept
A class cannot have multiple instances. Once created, the next time onward, you use only the existing instance. This approach helps you restrict unnecessary object creations in a centralized system. The approach also promotes easy maintenance.
Real-World Example
Let’s assume that you are a member of a sports team, and your team is participating in a tournament. Your team needs to play against multiple opponents throughout the tournament. Before each of these matches, as per the rules of the game, the captains of the two sides must do a coin toss. If your team does not have a captain, you need to elect someone as a captain. Prior to each game and each coin toss, you may not repeat the process of electing a captain if you already nominated a person as a captain for the team. Basically, from every team member’s perspective, there should be only one captain of the team.
Computer-World Example
In some specific software systems, you may prefer to use only one file system for the centralized management of resources. Also, this pattern can implement a caching mechanism.
Note
You notice a similar pattern when you consider the getRuntime( ) method of the java.lang.Runtime class. It is implemented as an eager initialization of a Singleton class. You’ll learn about eager initialization shortly.
Illustration
The constructor is private to prevent the use of a “new” operator.
You’ll create an instance of the class, if you did not create any such instance earlier; otherwise, you’ll simply reuse the existing one.
To take care of thread safety, I use the “synchronized” keyword.
Class Diagram
Package Explorer View
Discussion
I have shown you a simple example to illustrate the concept of the singleton pattern.
The constructor is private, so you cannot instantiate the Singleton class(Captain) outside. It helps us to refer the only instance that can exist in the system, and at the same time, you restrict the additional object creation of the Captain class.
The private constructor also ensures that the Captain class cannot be extended. So, subclasses cannot misuse the concept.
I used the “synchronized” keyword. So, multiple threads cannot involve in the instantiation process at the same time. I am forcing each thread to wait its turn to get the method, so thread- safety is ensured. But synchronization is a costly operation and once the instance is created, it is an additional overhead. (I’ll discuss some alternative methods in the upcoming sections, but each of them has its own pros and cons).
Implementation
Output
Q&A Session
Is this understanding correct?
And if they see that the instance is not created yet, each of them will try to create a new instance. As a result, you may end up with multiple instances of the class.
2. Why did you use the term lazy initialization in the code?
Because the singleton instance will not be created until the getCaptain() method is called here.
3. What do you mean by lazy initialization?
In simple terms, lazy initialization is a technique through which you delay the object creation process. It says that you should create an object only when it is required. This approach can be helpful when you deal with expensive processes to create an object.
4. Why are you making the class final? You have a private constructor that could prevent the inheritance. Is this correct?
Now notice the output.
Output
5. Are there any alternative approaches for modelling singleton design patterns?
There are many approaches. Each has its own pros and cons. You have already have seen two of them. Let’s discuss some alternative approaches.
Eager Initialization
Discussion
An eager initialization approach has the following pros and cons.
It is straightforward and cleaner.
It is the opposite of lazy initialization but still thread safe.
It has a small lag time when the application is in execution mode because everything is already loaded in memory.
The application takes longer to start (compared to lazy initialization) because everything needs to be loaded first. To examine the penalty, let’s add a dummy method (shown in bold) in the Singleton class. Notice that in the main method, I am invoking only this dummy method. Now examine the output.
Output
Analysis
Notice that A captain is elected for your team still appears in the output, though you may have no intention to deal with that.
So, in the preceding situation, an object of the Singleton class is always instantiated. Also, prior to Java 5, there were many issues that dealt with Singleton classes.
Bill Pugh’s Solution
This method does not use a synchronization technique and eager initialization. Notice that the SingletonHelper class comes into consideration only when someone invokes the getCaptain() method . And this approach will not create any unwanted output if you just call any dummyMethod() from main(), as with the previous case (to examine the result, you need to uncomment the dummyMethod() body). It is also treated one of the common and standard methods for implementing singletons in Java.
Double-Checked Locking
There is another popular approach, which is called double-checked locking. If you notice our synchronized implementation of the singleton pattern, you may find that synchronization operations are costly in general and the approach is useful for some initial threads that might break the singleton implementation. But in later phases, the synchronization operations might create additional overhead. To avoid that problem, you can use a synchronized block inside an if condition, as shown in the following code, to ensure that no unwanted instance is created.
If you are further interested in singleton patterns, read the article at www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples .
6. In short, if I need to create synchronized code, I can use the synchronized keyword in Java. Is this correct?
Yes, JVM ensures this. Internally, it uses locks on a class or an object to ensure that only one thread is accessing the data. In Java, you can apply this keyword to a method or statements(or, block of code). In this chapter, I have exercised it in both ways. (In the initial implementation, you used the synchronized method, and in double-checked locking, you saw the use of the other version).
In real-world scenarios, object creations are treated as costly operations.
Sometimes you need to implement a centralized system for easy maintenance, because it can help you provide a global access mechanism.
8. When should I consider singleton patterns?
Use of a pattern depends on particular use cases. But in general, you can consider singleton patterns to implement a centralized management system, to maintain a common log file, to maintain thread pools in a multithreaded environment, to implement caching mechanism or device drivers, and so forth.
9. I have some concern about the eager initialization example. Following the definition, it appears that it is not exactly eager initialization. This class is loaded by the JVM only when it is referenced by code during execution of the application. That means this is also lazy initialization. Is this correct?
Yes, to some extent your observation is correct. There is a debate on this discussion. In short, it is eager compared to the previous approaches. You saw that when you called only the dummyMethod() ; still, you instantiated the singleton, though you did not need it. So, in a context like this, it is eager but it is lazy in the sense that the singleton instantiation will not occur until the class is initialized. So, the degree of laziness is the key concern here.