Q)What is a singleton class?
A)In simple words, A
singleton class is a class which can have only one instance at any point of
time throughout the application and provides a global point of access to its
instance.
Q)Ok, How can you create a singleton class?
A)
class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton() {
}
}
This is a simple example for a singleton class where I used
a public static final INSTANCE variable and
initialized it with a Singleton object. Since INSTANCE is public static final, it can’t be reassigned and it is thread safe as well.
initialized it with a Singleton object. Since INSTANCE is public static final, it can’t be reassigned and it is thread safe as well.
Singleton’s constructor is made private to prevent the
instantiation of Singleton class from other classes. That means no other class
can create an object for Singleton class with the new() operator and also no other class can extend this Singleton
class with extends keyword. If any
class wants to have an instance of Singleton it can have by accessing INSTANCE
field of Singleton class like Singelton.INSTANCE as its static.
Q) So here, Singleton instance will be created at the time
of class loading that means when the Singleton class is loaded by JVM into memory.
So it’s an early loading/instantiation of Singleton class and it occupies
memory even when its not required yet, so what If we want this singleton
instance to be created when it’s really required?
A) I understand that you are interested in lazy loading/instantiation
of Singleton. Here it is
class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static synchronized Singleton
getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
Q) You synchronized the
whole method
public static synchronized Singleton
getInstance()
Don’t you see any performance overhead with this approach?
Only
one thread can access the method getInstance at a time to get the Singleton
instance and rest of all other threads need to wait even though INSTANCE is not
null as its already created.
A)Hmm..Yeah. Let me create it using synchronized block
instead of synchronized method, here how it goes
class Singleton {
private static Singleton INSTANCE;
private Singleton() {
}
public static Singleton
getInstance() {
if (INSTANCE == null) { //----- 1st
synchronized (Singleton.class) {
if (INSTANCE == null) { //----- 2nd
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
Q) Ok, but why do you use null check twice here?
A) I think I don’t need to explain you why did I use first
null check, as you know I am going to create Singleton instance in case
INSTANCE is null otherwise I ll return the already created instance.
2nd null check is being used to ensure thread
safety.
For suppose, 2 threads are concurrently trying to get the access for
getInstance() and two threads are gone through the 1st null check
and trying to enter into synchronized block, but only one thread(assume
thread-1) got the lock on Singleton.class’s object and enters the synchronized block while
the other thread (assume thread-2)waiting for the lock. Once thread-1 went
through 2nd null check then it creates a Singleton instance as
INSTANCE is null and leaves the synchronized block and thus releases lock. Now,
the waiting thread ( thread-2) holds the lock and enters the synchronized
block.
If there is no 2nd null check there then there is
a chance for the thread-2 to create another instance of Singleton class and
thus it breaks the singleton here. For that reason we have 2nd null
check here. So the thread-2 sees that INSTANCE is not null and comes out from
the synchronized block without creating Singleton instance.
Q) Ok. So double checking is for thread safety of a singleton.
A) yes
Q)Do you think that, this Singleton class is still singleton?
I can break your singleton with the help of reflection like
below
public class Test {
public static void main(String args[])
{
Singleton
singleton1 = Singleton.getInstance();
Singleton
singleton2 = null;
try {
// Test is package name contains Singleton class
Class
singletonClass = Class.forName("Test.Singleton");
// as we know there is only one private constructor
Constructor
cons = singletonClass.getDeclaredConstructor();
cons.setAccessible(true);
singleton2
= (Singleton) cons.newInstance();
System.out.println(singleton1
== singleton2 ? "Objects are equal" : "Objects are different");
}
catch
(ClassNotFoundException | InstantiationException
|
IllegalAccessException | IllegalArgumentException
|
InvocationTargetException | NoSuchMethodException
| SecurityException e) {
e.printStackTrace();
}
}
}
A) In that case I can implement Singleton like below for not
to be broken with reflection
class Singleton {
private static Singleton INSTANCE;
private Singleton() {
if (INSTANCE != null)
throw new RuntimeException(
"Singleton can
not be created more than once.");
}
public static Singleton
getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
Private constructor will be called to instantiate the
Singleton object through reflection. So we can throw some exception If INSTANCE
field is not null (that means its already created).
Q) Hmm, So you think this implementation saves your
Singleton from being broken from reflection. In my previous program, I am trying
to create a Singleton object (singleton1) with the help of getInstance() and after
that trying to create another object (singleton2) with the help of reflection.
What happens If I do this in reverse like below
public class Test {
public static void main(String args[])
{
// Singleton
singleton1 = Singleton.getInstance();
Singleton
singleton2 = null;
try {
// Test is package name contains Singleton class
Class
singletonClass = Class.forName("Test.Singleton");
// as we know there is only one private constructor
Constructor
cons = singletonClass.getDeclaredConstructor();
cons.setAccessible(true);
singleton2
= (Singleton) cons.newInstance();
Singleton
singleton1 = Singleton.getInstance();
System.out.println(singleton1
== singleton2 ? "Objects are equal" : "Objects are different");
}
catch
(ClassNotFoundException | InstantiationException
| IllegalAccessException |
IllegalArgumentException
|
InvocationTargetException | NoSuchMethodException
|
SecurityException e) {
e.printStackTrace();
}
}
}
Output:
Objects are different
Here, If I try to create a Singleton object (singleton2)
with the help of reflection first then it can proceed to create an Singleton
object as the static field INSTANCE is still null at that time as getInstance()
is not yet invoked. So we are allowed to create an many as objects we want for
the Singleton using reflection before getInstance() is invoked and thus breaks
the singleton. What do you say?
A) Yeah, you are right. Let me come to you with slightly modified
implementation of Singleton which protects itself from being broken with
reflection.
class Singleton {
private static Singleton INSTANCE;
private static boolean instanceCreated = false;
private Singleton() {
if (INSTANCE != null || instanceCreated)
throw new RuntimeException(
"Singleton can
not be created more than once.");
instanceCreated = true;
INSTANCE = this;
}
public static Singleton
getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
Here I created a Boolean static variable “InstanceCreated” and initialized
it with default value “false” and set it to “true” in the constructor when the instance
is created through the constructor for the first time. I also initialize
the INSTANCE field with the “this” object in the constructor to hold the
reference of Singleton when its first created through the reflection.
Q)Ok. Seems good, but
I can also break your singleton using serialization like below by assuming your class or your class’s parent class
implements Serializable interface
public class Test {
public static void main(String args[])
{
Singleton
singleton1 = Singleton.getInstance();
Singleton
singleton2 = null;
ObjectOutputStream
out = null;
ObjectInputStream
in = null;
try {
out
= new ObjectOutputStream(new FileOutputStream("serialize.file"));
out.writeObject(singleton1);
in
= new ObjectInputStream(new FileInputStream("serialize.file"));
singleton2
= (Singleton) in.readObject();
System.out.println(singleton1
== singleton2 ? "Objects are equal"
:
"Objects
are different");
}
catch (IOException |
ClassNotFoundException e) {
e.printStackTrace();
}
finally {
try {
out.close();
in.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
A) To protect my
Singleton from being broken with serialization, I can implement this way
class Singleton implements Serializable {
private static final long serialVersionUID = 1L;
private static Singleton INSTANCE;
private static boolean instanceCreated = false;
private Singleton() {
if (INSTANCE != null || instanceCreated)
throw new RuntimeException(
"Singleton can
not be created more than once.");
instanceCreated = true;
}
public static Singleton
getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
private Object readResolve()
{
return INSTANCE;
}
}
Just look at readResolve() method which has been defined in
Singleton class.
private Object readResolve()
readResolve() method is called just after the readObject()
method and just before returning the prepared object from ObjectInputStream to
the caller while deserialization. So here we defined readResolve() and returning the same INSTANCE variable which
thus being returned as a result of serialization instead of new serialized
object.
Q)Ohh Ok, I can still break your singleton by cloning
(assuming your Singleton extends another
class which implements Cloneable)
class A implements Cloneable {
@Override
public Object clone() throws
CloneNotSupportedException {
return super.clone();
}
}
Assume your Singleton class extends the class A and I can
create another Singleton instance with the help of clone() like below
public class Test {
public static void main(String args[])
{
Singleton
singleton1 = Singleton.getInstance();
Singleton
singleton2 = null;
try {
singleton2
= (Singleton) singleton1.clone();
System.out.println(singleton1
== singleton2 ? "Objects are equal"
:
"Objects
are different");
}
catch
(CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
A) Ok, in that case I can protect my Singleton by overriding
clone() method and throws
“CloneNotSupportedException” like below
class Singleton extends A implements Serializable {
private static final long serialVersionUID = 1L;
private static Singleton INSTANCE;
private static boolean instanceCreated = false;
private Singleton() {
if (INSTANCE != null || instanceCreated)
throw new RuntimeException(
"Singleton can
not be created more than once.");
instanceCreated = true;
}
public static Singleton
getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
private Object readResolve()
{
return new Object();
}
@Override
public Object clone() throws
CloneNotSupportedException {
throw new
CloneNotSupportedException();
}
}
Q)Hmm, Ok. Good. Lets meet again.
A) Sure.
Nice article on Singleton pattern Ganesh, really well compiled...
ReplyDeleteAsking qestіons are truly pleasant thing if you are not understandiing anything totallʏ, except this pοst
ReplyDeletepresents fastidious understandіng even.