© Vaskaran Sarcar 2019
Vaskaran SarcarJava Design Patternshttps://doi.org/10.1007/978-1-4842-4078-6_1

1. Singleton Pattern

Vaskaran Sarcar1 
(1)
Bangalore, Karnataka, India
 

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

These are the key characteristics in the following implementation.
  • 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

Figure 1-1 shows the class diagram for the illustration of the singleton pattern.
../images/395506_2_En_1_Chapter/395506_2_En_1_Fig1_HTML.jpg
Figure 1-1

Class diagram

Package Explorer View

Figure 1-2 shows the high-level structure of the program.
../images/395506_2_En_1_Chapter/395506_2_En_1_Fig2_HTML.jpg
Figure 1-2

Package Explorer view

Discussion

I have shown you a simple example to illustrate the concept of the singleton pattern.

Let’s review the notable characteristics with the following approach.
  • 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

Here’s the implementation.
package jdp2e.singleton.demo;
final class Captain
{
    private static Captain captain;
    //We make the constructor private to prevent the use of "new"
    private Captain() {    }
    public static synchronized Captain getCaptain()
    {
        // Lazy initialization
        if (captain == null)
        {
            captain = new Captain();
            System.out.println("New captain is elected for your team.");
        }
        else
        {
            System.out.print("You already have a captain for your team.");
            System.out.println("Send him for the toss.");
        }
        return captain;
    }
}
// We cannot extend Captain class.The constructor is private in this case.
//class B extends Captain{}// error
public class SingletonPatternExample {
    public static void main(String[] args) {
        System.out.println("***Singleton Pattern Demo*** ");
        System.out.println("Trying to make a captain for your team:");
        //Constructor is private.We cannot use "new" here.
        //Captain c3 = new Captain();//error
        Captain captain1 = Captain.getCaptain();
        System.out.println("Trying to make another captain for your team:");
        Captain captain2 = Captain.getCaptain();
        if (captain1 == captain2)
        {
            System.out.println("captain1 and captain2 are same instance.");
        }
    }
}

Output

Here’s the output.
***Singleton Pattern Demo***
Trying to make a captain for your team:
New captain is elected for your team.
Trying to make another captain for your team:
You already have a captain for your team.Send him for the toss.
captain1 and captain2 are same instance.

Q&A Session

    1.    Why are you complicating the program? You could simply write the Singleton class as follows.
class Captain
{
   private static Captain captain;
    //We make the constructor private to prevent the use of "new"
   private Captain() { }
   public static Captain getCaptain()
   {
             // Lazy initialization
             if (captain == null)
             {
                captain = new Captain();
                System.out.println("New captain is elected for your team.");
             }
             else
             {
                 System.out.print("You already have a captain for your team.");
                 System.out.println("Send him for the toss.");
             }
             return captain;
      }
}

Is this understanding correct?

It can work in a single threaded environment only but it cannot be considered a thread-safe implementation in a multithreaded environment. Consider the following case. Suppose, in a multithreaded environment, two (or more) threads try to evaluate this:
if (captain == null)

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?

Subclassing can be prevented in various ways. Yes, in this example, since the constructor is already marked with the “private” keyword, it was not needed. But if you make the Captain class final, as shown in the example, that approach is considered a better practice. It is effective when you consider a nested class. For example, let’s modify the private constructor body to examine the number of instances (of the Captain class) created. Let’s further assume that in the preceding example, you have a non-static nested class (called inner class in Java) like the following. (All changes are shown in bold.)
//final class Captain
class Captain
{
    private static Captain captain;
    //We make the constructor private to prevent the use of "new"
    static int numberOfInstance=0;
    private Captain()
    {
        numberOfInstance++;
        System.out.println("Number of instances at this moment="+ numberOfInstance);
    }
    public static synchronized Captain getCaptain()
    {
        // Lazy initialization
        if (captain == null)
        {
            captain = new Captain();
            System.out.println("New captain is elected for your team.");
        }
        else
        {
            System.out.print("You already have a captain for your team.");
            System.out.println("Send him for the toss.");
        }
        return captain;
    }
     //A non-static nested class (inner class)
   public class CaptainDerived extends Captain
   {
     //Some code
   }
}
Now add an another line of code (shown in bold) inside the main() method , as follows.
public class SingletonPatternExample {
    public static void main(String[] args) {
        System.out.println("***Singleton Pattern Demo*** ");
        System.out.println("Trying to make a captain for your team:");
        //Constructor is private.We cannot use "new" here.
        //Captain c3 = new Captain();//error
        Captain captain1 = Captain.getCaptain();
        System.out.println("Trying to make another captain for your team:");
        Captain captain2 = Captain.getCaptain();
        if (captain1 == captain2)
        {
            System.out.println("captain1 and captain2 are same instance.");
        }
        Captain.CaptainDerived derived=captain1.new CaptainDerived();
    }
}

Now notice the output.

Output

Now, you can see that the program has violated the key objective, because I never intended to create more than one instance.
***Singleton Pattern Demo***
Trying to make a captain for your team:
Number of instances at this moment=1
New captain is elected for your team.
Trying to make another captain for your team:
You already have a captain for your team.Send him for the toss.
captain1 and captain2 are same instance.
Number of instances at this moment=2

    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

Here is a sample implementation of the eager initialization.
class Captain
{
    //Early initialization
    private static final Captain captain = new Captain();
    //We make the constructor private to prevent the use of "new"
    private Captain()
    {
        System.out.println("A captain is elected for your team.");
    }
    /* Global point of access.The method getCaptain() is a public static method*/
    public static Captain getCaptain()
    {
        System.out.println("You have a captain for your team.");
        return captain;
    }
}

Discussion

An eager initialization approach has the following pros and cons.

Pros
  • 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.

Cons
  • 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.

package jdp2e.singleton.questions_answers;
class Captain
{
    //Early initialization
    private static final Captain captain = new Captain();
    //We make the constructor private to prevent the use of "new"
    private Captain()
    {
        System.out.println("A captain is elected for your team.");
    }
    /* Global point of access.The method getCaptain() is a public static method*/
    public static Captain getCaptain()
    {
        System.out.println("You have a captain for your team.");
        return captain;
    }
    public static void dummyMethod()
    {
        System.out.println("It is a dummy method");
    }
}
public class EagerInitializationExample {
    public static void main(String[] args) {
        System.out.println("***Singleton Pattern Demo With Eager Initialization*** ");
        Captain.dummyMethod();
        /*System.out.println("Trying to make a captain for your team:");
        Captain captain1 = Captain.getCaptain();
        System.out.println("Trying to make another captain for your team:");
        Captain captain2 = Captain.getCaptain();
            if (captain1 == captain2)
            {
                System.out.println("captain1 and captain2 are same instance.");
            }*/
    }
}

Output

***Singleton Pattern Demo With Eager Initialization***
A captain is elected for your team.
It is a dummy method

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

Bill Pugh came up with a different approach using a static nested helper class.
package jdp2e.singleton.questions_answers;
class Captain1
{
    private Captain1() {
        System.out.println("A captain is elected for your team.");
    }
    //Bill Pugh solution
    private static class SingletonHelper{
        /*Nested class is referenced after getCaptain() is called*/
        private static final Captain1 captain = new Captain1();
    }
    public static Captain1 getCaptain()
    {
        return SingletonHelper.captain;
    }
    /*public static void dummyMethod()
    {
        System.out.println("It is a dummy method");
    }  */
}

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.

package jdp2e.singleton.questions_answers;
final class Captain2
{
    private static Captain2 captain;
    //We make the constructor private to prevent the use of "new"
    static int numberOfInstance=0;
    private Captain2() {
        numberOfInstance++;
        System.out.println("Number of instances at this moment="+ numberOfInstance);
    }
    public static  Captain2 getCaptain(){
        if (captain == null) {
            synchronized (Captain2.class) {
                // Lazy initialization
                if (captain == null){
                    captain = new Captain2();
                    System.out.println("New captain is elected for your team.");
                }
                else
                {
                    System.out.print("You already have a captain for your team.");
                    System.out.println("Send him for the toss.");
                }
            }
        }
        return captain;
    }
}

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).

    7.    Why are multiple object creations a big concern?
  • 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.71.28