Singleton implementation in JAVA
In this article I want to touch on the theme of one of the most common patterns of object-oriented programming - Singleton. But in this case, I will not describe the advantages / disadvantages and areas of application of this pattern, but try to express my view on its implementation in JAVA.
Background
The Singleton pattern ensures that a class has only one instance and provides a global access point to it.
Scope
1.) The system must have at most one instance of a given class.
2.) The instance should be easily accessible to all clients of this class.
3.) Creating an on demand object, that is, when it is needed for the first time, and not during the initialization of the system.
Implementation (JAVA):
At the moment, there are several implementation options with their own disadvantages and advantages. We’ll try to figure them out now.
Option one is the simplest one that comes to mind immediately after understanding the problem.

This solution has the only drawback - it does not work in a multi-threaded environment and therefore is not suitable in most cases. The solution is exclusively suitable for single-threaded applications.
It does not matter, you say, and propose the following solution.
Option two:

And you will be right, since we solved the multithreading problem, but lost two important things:
1. Lazy initialization (the instance object will be created by the classloader during class initialization)
2. There is no way to handle exceptions during constructor call.
The solution is suitable for multi-threaded applications, provided there is no danger of exceptional situations in the designer and there is no need for lazy initialization.
Next, 2 solutions arise.
1) Using the internal class (decision Bill Pugh ( Bill Pugh ) "the Initialization on Demand device Holder" ).
2.) Using synchronization.
Let's start with Bill Pew.
Option three:
“Initialization on Demand Holder”

In this case, we completely solved the problem of lazy initialization - the object is initialized the first time the getInstance () method is called. But we still have a problem with exception handling in the constructor. So, if the class constructor does not raise fears of creating exceptional situations, then you can safely use this method.
Synchronization
I would like to pay special attention to this part. One could approach this issue with the heading “synchronized - myths and reality”.
And so, the most straightforward method.
Option Four:

This option has only one drawback. Synchronization is useful only once, the first time you access getInstance (), after that, every time you access this method, synchronization simply takes time. What can be said about this? Well, firstly, if the call to getInstance () does not happen often enough (which means “often enough” is up to you), then this method has an advantage over the others - it is simple, understandable, lazily initialized, and makes it possible to handle exceptional situations in the constructor. And secondly, synchronization in Java has ceased to be burdensomely slow as much as it is feared. Well, what else is needed for happiness?
Now consider a synchronized solution, in which we will try to solve the problem that arose in the previous version.
The most common method is Double-Checked Locking . In its original version:

Does not work! Why? This is a separate topic, but for those interested, I can advise you to read this article http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html .
But do not despair at all, in JAVA 5 they solved the problem using the volatile modifier . At the moment, the solution looks like this:
Option Five:

Despite the fact that this option looks like the perfect solution, it is not recommended to use it. fellow Allen Holub remarked that using volatilemodifier can lead to performance problems on multiprocessor systems. But it's up to you to decide.
Here, in general, and all the common options for implementing this pattern. But, Singleton’s pitfalls do not end there. There are a few more points to consider when designing a particular application using Singleton.
Pitfalls
1. Inheritance
In the vast majority of cases in Singleton classes, inheritance is not necessary and, moreover, unnecessarily is the result of over-design. Yes, and the implementation of inheritance has certain difficulties, given that both the instance itself and the getInstance () method are static.
Therefore, I recommend using the final modifier and forbid the inheritance of this class, unless there is a special need for the opposite.
2. Two or more virtual machines
Each virtual machine creates its own copy of the Singleton object. Although it seems obvious at first glance, in many distributed systems such as EJB, JINI, and RMI, things are not so simple. When intermediate levels hide (make transparent) distributed technologies, it is difficult to say where and when an object is initialized.
3. Various Class Loaders
When 2 class loaders load a class, each of them can create its own copy of Singleton (in those cases when instance is initialized with a class loader). This is especially true when using servlets, since in some application server implementations each servlet has its own class loader.
There are a number of problems that are less relevant (such as reflection technology and the implementation of the Cloneable and Serializable interfaces), and will not be considered by me due to their exotic nature in the application of Singleton classes. But, in any case, I will be happy to answer any questions to this material.
Here, in general, and all the key points that I wanted to cover in this article. It remains only to note that this material does not claim to be the ultimate truth and allows the existence of points of view that differ from the point of view of the author. And even more than that, any comments are welcome and will be pleasant to note.
Thanks for attention.
Background
The Singleton pattern ensures that a class has only one instance and provides a global access point to it.
Scope
1.) The system must have at most one instance of a given class.
2.) The instance should be easily accessible to all clients of this class.
3.) Creating an on demand object, that is, when it is needed for the first time, and not during the initialization of the system.
Implementation (JAVA):
At the moment, there are several implementation options with their own disadvantages and advantages. We’ll try to figure them out now.
Option one is the simplest one that comes to mind immediately after understanding the problem.

This solution has the only drawback - it does not work in a multi-threaded environment and therefore is not suitable in most cases. The solution is exclusively suitable for single-threaded applications.
It does not matter, you say, and propose the following solution.
Option two:

And you will be right, since we solved the multithreading problem, but lost two important things:
1. Lazy initialization (the instance object will be created by the classloader during class initialization)
2. There is no way to handle exceptions during constructor call.
The solution is suitable for multi-threaded applications, provided there is no danger of exceptional situations in the designer and there is no need for lazy initialization.
Next, 2 solutions arise.
1) Using the internal class (decision Bill Pugh ( Bill Pugh ) "the Initialization on Demand device Holder" ).
2.) Using synchronization.
Let's start with Bill Pew.
Option three:
“Initialization on Demand Holder”

In this case, we completely solved the problem of lazy initialization - the object is initialized the first time the getInstance () method is called. But we still have a problem with exception handling in the constructor. So, if the class constructor does not raise fears of creating exceptional situations, then you can safely use this method.
Synchronization
I would like to pay special attention to this part. One could approach this issue with the heading “synchronized - myths and reality”.
And so, the most straightforward method.
Option Four:

This option has only one drawback. Synchronization is useful only once, the first time you access getInstance (), after that, every time you access this method, synchronization simply takes time. What can be said about this? Well, firstly, if the call to getInstance () does not happen often enough (which means “often enough” is up to you), then this method has an advantage over the others - it is simple, understandable, lazily initialized, and makes it possible to handle exceptional situations in the constructor. And secondly, synchronization in Java has ceased to be burdensomely slow as much as it is feared. Well, what else is needed for happiness?
Now consider a synchronized solution, in which we will try to solve the problem that arose in the previous version.
The most common method is Double-Checked Locking . In its original version:

Does not work! Why? This is a separate topic, but for those interested, I can advise you to read this article http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html .
But do not despair at all, in JAVA 5 they solved the problem using the volatile modifier . At the moment, the solution looks like this:
Option Five:

Despite the fact that this option looks like the perfect solution, it is not recommended to use it. fellow Allen Holub remarked that using volatilemodifier can lead to performance problems on multiprocessor systems. But it's up to you to decide.
Here, in general, and all the common options for implementing this pattern. But, Singleton’s pitfalls do not end there. There are a few more points to consider when designing a particular application using Singleton.
Pitfalls
1. Inheritance
In the vast majority of cases in Singleton classes, inheritance is not necessary and, moreover, unnecessarily is the result of over-design. Yes, and the implementation of inheritance has certain difficulties, given that both the instance itself and the getInstance () method are static.
Therefore, I recommend using the final modifier and forbid the inheritance of this class, unless there is a special need for the opposite.
2. Two or more virtual machines
Each virtual machine creates its own copy of the Singleton object. Although it seems obvious at first glance, in many distributed systems such as EJB, JINI, and RMI, things are not so simple. When intermediate levels hide (make transparent) distributed technologies, it is difficult to say where and when an object is initialized.
3. Various Class Loaders
When 2 class loaders load a class, each of them can create its own copy of Singleton (in those cases when instance is initialized with a class loader). This is especially true when using servlets, since in some application server implementations each servlet has its own class loader.
There are a number of problems that are less relevant (such as reflection technology and the implementation of the Cloneable and Serializable interfaces), and will not be considered by me due to their exotic nature in the application of Singleton classes. But, in any case, I will be happy to answer any questions to this material.
Here, in general, and all the key points that I wanted to cover in this article. It remains only to note that this material does not claim to be the ultimate truth and allows the existence of points of view that differ from the point of view of the author. And even more than that, any comments are welcome and will be pleasant to note.
Thanks for attention.